swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.58k stars 10.36k forks source link

[SR-15481] JSON encoding/decoding error on linux #57786

Open swift-ci opened 2 years ago

swift-ci commented 2 years ago
Previous ID SR-15481
Radar rdar://problem/85408307
Original Reporter editfmah (JIRA User)
Type Bug
Environment Digital Ocean Droplet Ubuntu Linux 20.04 Swift 5.5.1
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: c886bfe0eef50a04b68246dfd1b54ac3

Issue Description:

When using a dictionary that contains an array as a value, and it is empty but not nil, the encoder encodes it as if it was an array and not a dictionary.

Code to replicate:

// replication example

class Test : Codable {
    init(){}
    var id: UUID = UUID()
    var data: [UUID:[String]] = [:]
}

if let encodedObject = try? JSONEncoder().encode(Test()) {
    print("encoded json:\n\(String(data: encodedObject, encoding: .utf8)!)")
    do {
        let reformedObject = try JSONDecoder().decode(Test.self, from: encodedObject)
        print("worked")
    } catch {
        print("failed")
        print("json decode error: \(error)")
    }
}

Output on MacOS 12.0.1:

encoded json:
{"id":"D320770C-0B69-4FC0-8854-3FE4A8F9EA3A","data":[]}
worked

Output on Ubuntu Linux 20.04:

encoded json:
{"data":{},"id":"0CA4125F-95DF-4D70-A6AD-3B661908FC29"}
failed
json decode error: typeMismatch(Swift.Array<Foundation.JSONValue>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil)], debugDescription: "Expected to decode Array<JSONValue> but found a dictionary instead.", underlyingError: nil))

In reality both are wrong. MacOS accepts an Array where it should be a dictionary: And Linux Expects and array but encodes it correctly as a dictionary.

When the object is populated with data, the encoding/decoding works as expected:

Revised class for populated/working code:

class Test : Codable {
    init(){}
    var id: UUID = UUID()
    var data: [UUID:[String]] = [UUID():["AnyOldDataCanGoHere"]]
}

MacOS output:

encoded json:
{"id":"81A41826-6F00-42D7-91AE-264E5983FE53","data":["F478B1A5-7CB9-4F3C-B8D1-41ADFBA83108",["AnyOldDataCanGoHere"]]}
worked

NOTE: Still outputting as an array and not a dictionary

Linux output:

encoded json:
{"id":"91DDC7F7-DF4D-4DFD-8616-BD59BB3E8902","data":["96293D09-2046-4918-8CD6-1A4082AB1242",["AnyOldDataCanGoHere"]]}
worked

Incorrect encoding (`[]` not `{}`), correct behaviour.

Additional debugging/research:

If we change the format of the property to

var data: [String:[String]] = [:]

, then this is the output from both platforms, and everything works as expected:

encoded json:
 {"data":{},"id":"F1DAADFD-F836-4F42-8932-CE1596B583B9"}
 worked

This is similar in output to:

https://bugs.swift.org/browse/SR-5312

But different scenario, and setup. E.g. even with String keys the encoder uses '[ ]' and not '{ }'.

weissi commented 2 years ago

@swift-ci create