Skip to content

Commit ac78023

Browse files
authored
Merge pull request #3138 from millenomi/sr-15781-fix-empty-complex-dictionary-json
Prevent empty dictionary with non-String keys from encoding as a JSON object.
2 parents b86ea32 + 6dadb2e commit ac78023

File tree

2 files changed

+36
-2
lines changed

2 files changed

+36
-2
lines changed

Sources/Foundation/JSONEncoder.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,8 +504,8 @@ extension _SpecialTreatmentEncoder {
504504
return .string(url.absoluteString)
505505
case let decimal as Decimal:
506506
return .number(decimal.description)
507-
case let object as [String: Encodable]:
508-
return try self.wrapObject(object, for: additionalKey)
507+
case let object as _JSONStringDictionaryEncodableMarker:
508+
return try self.wrapObject(object as! [String: Encodable], for: additionalKey)
509509
default:
510510
let encoder = self.getEncoder(for: additionalKey)
511511
try encodable.encode(to: encoder)

Tests/Foundation/Tests/TestJSONEncoder.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,39 @@ class TestJSONEncoder : XCTestCase {
869869
XCTAssertEqual(JSONEncoder.OutputFormatting.withoutEscapingSlashes.rawValue, 8)
870870
}
871871

872+
func test_SR17581_codingEmptyDictionaryWithNonstringKeyDoesRoundtrip() throws {
873+
struct Something: Codable {
874+
struct Key: Codable, Hashable {
875+
var x: String
876+
}
877+
878+
var dict: [Key: String]
879+
880+
enum CodingKeys: String, CodingKey {
881+
case dict
882+
}
883+
884+
init(from decoder: Decoder) throws {
885+
let container = try decoder.container(keyedBy: CodingKeys.self)
886+
self.dict = try container.decode([Key: String].self, forKey: .dict)
887+
}
888+
889+
func encode(to encoder: Encoder) throws {
890+
var container = encoder.container(keyedBy: CodingKeys.self)
891+
try container.encode(dict, forKey: .dict)
892+
}
893+
894+
init(dict: [Key: String]) {
895+
self.dict = dict
896+
}
897+
}
898+
899+
let toEncode = Something(dict: [:])
900+
let data = try JSONEncoder().encode(toEncode)
901+
let result = try JSONDecoder().decode(Something.self, from: data)
902+
XCTAssertEqual(result.dict.count, 0)
903+
}
904+
872905
// MARK: - Helper Functions
873906
private var _jsonEmptyDictionary: Data {
874907
return "{}".data(using: .utf8)!
@@ -1471,6 +1504,7 @@ extension TestJSONEncoder {
14711504
("test_dictionary_snake_case_decoding", test_dictionary_snake_case_decoding),
14721505
("test_dictionary_snake_case_encoding", test_dictionary_snake_case_encoding),
14731506
("test_OutputFormattingValues", test_OutputFormattingValues),
1507+
("test_SR17581_codingEmptyDictionaryWithNonstringKeyDoesRoundtrip", test_SR17581_codingEmptyDictionaryWithNonstringKeyDoesRoundtrip),
14741508
]
14751509
}
14761510
}

0 commit comments

Comments
 (0)