Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Sources/FoundationEssentials/JSON/JSONDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -610,8 +610,13 @@ extension JSONDecoderImpl: Decoder {
if type == Decimal.self {
return try self.unwrapDecimal(from: mapValue, for: codingPathNode, additionalKey) as! T
}
if T.self is _JSONStringDictionaryDecodableMarker.Type {
return try self.unwrapDictionary(from: mapValue, as: type, for: codingPathNode, additionalKey)
switch options.keyDecodingStrategy {
case .useDefaultKeys:
break
case .convertFromSnakeCase, .custom:
if T.self is _JSONStringDictionaryDecodableMarker.Type {
return try self.unwrapDictionary(from: mapValue, as: type, for: codingPathNode, additionalKey)
}
}

return try self.with(value: mapValue, path: codingPathNode.appending(additionalKey)) {
Expand Down
56 changes: 52 additions & 4 deletions Sources/FoundationEssentials/JSON/JSONEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1214,14 +1214,14 @@ private extension __JSONEncoder {
return self.wrap(url.absoluteString)
} else if let decimal = value as? Decimal {
return .number(decimal.description)
} else if let encodable = value as? _JSONStringDictionaryEncodableMarker {
} else if !options.keyEncodingStrategy.isDefault, let encodable = value as? _JSONStringDictionaryEncodableMarker {
return try self.wrap(encodable as! [String:Encodable], for: additionalKey)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we able to use _specializingCast here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, no, we can't use it here. _specializingCast internally performs type equality check, and here we use as? to check protocol conformance. so _specializingCast is inapplicable here

Copy link
Contributor Author

@ChrisBenua ChrisBenua Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@inline(__always)
internal func _specializingCast<Input, Output>(_ value: Input, to type: Output.Type) -> Output? {
  guard Input.self == Output.self else { return nil } // this check will always fail when we check for protocol conformance
  return _identityCast(value, to: type)
}

} else if let array = value as? _JSONDirectArrayEncodable {
} else if let directArrayEncodable = _asDirectArrayEncoding(value, for: additionalKey) {
if options.outputFormatting.contains(.prettyPrinted) {
let (bytes, lengths) = try array.individualElementRepresentation(encoder: self, additionalKey)
let (bytes, lengths) = try directArrayEncodable.individualElementRepresentation(encoder: self, additionalKey)
return .directArray(bytes, lengths: lengths)
} else {
return .nonPrettyDirectArray(try array.nonPrettyJSONRepresentation(encoder: self, additionalKey))
return .nonPrettyDirectArray(try directArrayEncodable.nonPrettyJSONRepresentation(encoder: self, additionalKey))
}
}

Expand All @@ -1246,6 +1246,43 @@ private extension __JSONEncoder {
return encoder.takeValue()
}

func _asDirectArrayEncoding<T: Encodable>(_ value: T, for additionalKey: (some CodingKey)? = _CodingKey?.none) -> _JSONDirectArrayEncodable? {
switch value {
case let array as [Int8]:
array
case let array as [Int16]:
array
case let array as [Int32]:
array
case let array as [Int64]:
array
case let array as [Int128]:
array
case let array as [Int]:
array
case let array as [UInt8]:
array
case let array as [UInt16]:
array
case let array as [UInt32]:
array
case let array as [UInt64]:
array
case let array as [UInt128]:
array
case let array as [UInt]:
array
case let array as [String]:
array
case let array as [Float]:
array
case let array as [Double]:
array
default:
nil
}
}

@inline(__always)
func getEncoder(for additionalKey: CodingKey?) -> __JSONEncoder {
if let additionalKey {
Expand Down Expand Up @@ -1466,3 +1503,14 @@ extension Array : _JSONDirectArrayEncodable where Element: _JSONSimpleValueArray
return (writer.bytes, lengths: byteLengths)
}
}

private extension JSONEncoder.KeyEncodingStrategy {
var isDefault: Bool {
switch self {
case .useDefaultKeys:
return true
case .custom, .convertToSnakeCase:
return false
}
}
}