Skip to content

Commit bf49563

Browse files
iCharlesHukperryua
andauthored
Provide concrete implementation of all decodeIfPresent API in KeyedContainerProtocol for JSONDecoder (#1003)
resolves: rdar://132423133 Co-authored-by: Kevin Perry <[email protected]>
1 parent 7bb4100 commit bf49563

File tree

4 files changed

+679
-9
lines changed

4 files changed

+679
-9
lines changed

Sources/FoundationEssentials/JSON/JSONDecoder.swift

Lines changed: 212 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,35 +1266,80 @@ extension JSONDecoderImpl {
12661266
return bool
12671267
}
12681268

1269+
func decodeIfPresent(_ type: Bool.Type, forKey key: K) throws -> Bool? {
1270+
guard let value = getValueIfPresent(forKey: key) else {
1271+
return nil
1272+
}
1273+
switch value {
1274+
case .null: return nil
1275+
case .bool(let result): return result
1276+
default: throw createTypeMismatchError(type: type, forKey: key, value: value)
1277+
}
1278+
}
1279+
12691280
func decode(_ type: String.Type, forKey key: K) throws -> String {
12701281
let value = try getValue(forKey: key)
12711282
return try impl.unwrapString(from: value, for: self.codingPathNode, key)
12721283
}
12731284

1285+
func decodeIfPresent(_ type: String.Type, forKey key: K) throws -> String? {
1286+
guard let value = getValueIfPresent(forKey: key) else {
1287+
return nil
1288+
}
1289+
switch value {
1290+
case .null: return nil
1291+
default: return try impl.unwrapString(from: value, for: self.codingPathNode, key)
1292+
}
1293+
}
1294+
12741295
func decode(_: Double.Type, forKey key: K) throws -> Double {
12751296
try decodeFloatingPoint(key: key)
12761297
}
12771298

1299+
func decodeIfPresent(_: Double.Type, forKey key: K) throws -> Double? {
1300+
try decodeFloatingPointIfPresent(key: key)
1301+
}
1302+
12781303
func decode(_: Float.Type, forKey key: K) throws -> Float {
12791304
try decodeFloatingPoint(key: key)
12801305
}
12811306

1307+
func decodeIfPresent(_: Float.Type, forKey key: K) throws -> Float? {
1308+
try decodeFloatingPointIfPresent(key: key)
1309+
}
1310+
12821311
func decode(_: Int.Type, forKey key: K) throws -> Int {
12831312
try decodeFixedWidthInteger(key: key)
12841313
}
12851314

1315+
func decodeIfPresent(_: Int.Type, forKey key: K) throws -> Int? {
1316+
try decodeFixedWidthIntegerIfPresent(key: key)
1317+
}
1318+
12861319
func decode(_: Int8.Type, forKey key: K) throws -> Int8 {
12871320
try decodeFixedWidthInteger(key: key)
12881321
}
12891322

1323+
func decodeIfPresent(_: Int8.Type, forKey key: K) throws -> Int8? {
1324+
try decodeFixedWidthIntegerIfPresent(key: key)
1325+
}
1326+
12901327
func decode(_: Int16.Type, forKey key: K) throws -> Int16 {
12911328
try decodeFixedWidthInteger(key: key)
12921329
}
12931330

1331+
func decodeIfPresent(_: Int16.Type, forKey key: K) throws -> Int16? {
1332+
try decodeFixedWidthIntegerIfPresent(key: key)
1333+
}
1334+
12941335
func decode(_: Int32.Type, forKey key: K) throws -> Int32 {
12951336
try decodeFixedWidthInteger(key: key)
12961337
}
12971338

1339+
func decodeIfPresent(_: Int32.Type, forKey key: K) throws -> Int32? {
1340+
try decodeFixedWidthIntegerIfPresent(key: key)
1341+
}
1342+
12981343
func decode(_: Int64.Type, forKey key: K) throws -> Int64 {
12991344
try decodeFixedWidthInteger(key: key)
13001345
}
@@ -1304,22 +1349,42 @@ extension JSONDecoderImpl {
13041349
try decodeFixedWidthInteger(key: key)
13051350
}
13061351

1352+
func decodeIfPresent(_: Int64.Type, forKey key: K) throws -> Int64? {
1353+
try decodeFixedWidthIntegerIfPresent(key: key)
1354+
}
1355+
13071356
func decode(_: UInt.Type, forKey key: K) throws -> UInt {
13081357
try decodeFixedWidthInteger(key: key)
13091358
}
13101359

1360+
func decodeIfPresent(_: UInt.Type, forKey key: K) throws -> UInt? {
1361+
try decodeFixedWidthIntegerIfPresent(key: key)
1362+
}
1363+
13111364
func decode(_: UInt8.Type, forKey key: K) throws -> UInt8 {
13121365
try decodeFixedWidthInteger(key: key)
13131366
}
13141367

1368+
func decodeIfPresent(_: UInt8.Type, forKey key: K) throws -> UInt8? {
1369+
try decodeFixedWidthIntegerIfPresent(key: key)
1370+
}
1371+
13151372
func decode(_: UInt16.Type, forKey key: K) throws -> UInt16 {
13161373
try decodeFixedWidthInteger(key: key)
13171374
}
13181375

1376+
func decodeIfPresent(_: UInt16.Type, forKey key: K) throws -> UInt16? {
1377+
try decodeFixedWidthIntegerIfPresent(key: key)
1378+
}
1379+
13191380
func decode(_: UInt32.Type, forKey key: K) throws -> UInt32 {
13201381
try decodeFixedWidthInteger(key: key)
13211382
}
13221383

1384+
func decodeIfPresent(_: UInt32.Type, forKey key: K) throws -> UInt32? {
1385+
try decodeFixedWidthIntegerIfPresent(key: key)
1386+
}
1387+
13231388
func decode(_: UInt64.Type, forKey key: K) throws -> UInt64 {
13241389
try decodeFixedWidthInteger(key: key)
13251390
}
@@ -1329,10 +1394,24 @@ extension JSONDecoderImpl {
13291394
try decodeFixedWidthInteger(key: key)
13301395
}
13311396

1397+
func decodeIfPresent(_: UInt64.Type, forKey key: K) throws -> UInt64? {
1398+
try decodeFixedWidthIntegerIfPresent(key: key)
1399+
}
1400+
13321401
func decode<T: Decodable>(_ type: T.Type, forKey key: K) throws -> T {
13331402
try self.impl.unwrap(try getValue(forKey: key), as: type, for: codingPathNode, key)
13341403
}
13351404

1405+
func decodeIfPresent<T: Decodable>(_ type: T.Type, forKey key: K) throws -> T? {
1406+
guard let value = getValueIfPresent(forKey: key) else {
1407+
return nil
1408+
}
1409+
switch value {
1410+
case .null: return nil
1411+
default: return try self.impl.unwrap(value, as: type, for: codingPathNode, key)
1412+
}
1413+
}
1414+
13361415
func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer<NestedKey> {
13371416
let value = try getValue(forKey: key)
13381417
return try impl.with(value: value, path: codingPathNode.appending(key)) {
@@ -1378,6 +1457,10 @@ extension JSONDecoderImpl {
13781457
return value
13791458
}
13801459

1460+
@inline(__always) private func getValueIfPresent(forKey key: some CodingKey) -> JSONMap.Value? {
1461+
dictionary[key.stringValue]
1462+
}
1463+
13811464
private func createTypeMismatchError(type: Any.Type, forKey key: K, value: JSONMap.Value) -> DecodingError {
13821465
return DecodingError.typeMismatch(type, .init(
13831466
codingPath: self.codingPathNode.path(byAppending: key), debugDescription: "Expected to decode \(type) but found \(value.debugDataTypeDescription) instead."
@@ -1393,6 +1476,26 @@ extension JSONDecoderImpl {
13931476
let value = try getValue(forKey: key)
13941477
return try self.impl.unwrapFloatingPoint(from: value, as: T.self, for: codingPathNode, key)
13951478
}
1479+
1480+
@inline(__always) private func decodeFixedWidthIntegerIfPresent<T: FixedWidthInteger>(key: Self.Key) throws -> T? {
1481+
guard let value = getValueIfPresent(forKey: key) else {
1482+
return nil
1483+
}
1484+
switch value {
1485+
case .null: return nil
1486+
default: return try self.impl.unwrapFixedWidthInteger(from: value, as: T.self, for: codingPathNode, key)
1487+
}
1488+
}
1489+
1490+
@inline(__always) private func decodeFloatingPointIfPresent<T: PrevalidatedJSONNumberBufferConvertible & BinaryFloatingPoint>(key: K) throws -> T? {
1491+
guard let value = getValueIfPresent(forKey: key) else {
1492+
return nil
1493+
}
1494+
switch value {
1495+
case .null: return nil
1496+
default: return try self.impl.unwrapFloatingPoint(from: value, as: T.self, for: codingPathNode, key)
1497+
}
1498+
}
13961499
}
13971500
}
13981501

@@ -1456,37 +1559,82 @@ extension JSONDecoderImpl {
14561559
return bool
14571560
}
14581561

1562+
mutating func decodeIfPresent(_ type: Bool.Type) throws -> Bool? {
1563+
let value = self.peekNextValueIfPresent(ofType: Bool.self)
1564+
let result: Bool? = switch value {
1565+
case nil, .null: nil
1566+
case .bool(let bool): bool
1567+
default: throw impl.createTypeMismatchError(type: type, for: self.currentCodingPath, value: value!)
1568+
}
1569+
advanceToNextValue()
1570+
return result
1571+
}
1572+
14591573
mutating func decode(_ type: String.Type) throws -> String {
14601574
let value = try self.peekNextValue(ofType: String.self)
14611575
let string = try impl.unwrapString(from: value, for: codingPathNode, currentIndexKey)
14621576
advanceToNextValue()
14631577
return string
14641578
}
14651579

1580+
mutating func decodeIfPresent(_ type: String.Type) throws -> String? {
1581+
let value = self.peekNextValueIfPresent(ofType: String.self)
1582+
let result: String? = switch value {
1583+
case nil, .null: nil
1584+
default: try impl.unwrapString(from: value.unsafelyUnwrapped, for: codingPathNode, currentIndexKey)
1585+
}
1586+
advanceToNextValue()
1587+
return result
1588+
}
1589+
14661590
mutating func decode(_: Double.Type) throws -> Double {
14671591
try decodeFloatingPoint()
14681592
}
14691593

1594+
mutating func decodeIfPresent(_ type: Double.Type) throws -> Double? {
1595+
try decodeFloatingPointIfPresent()
1596+
}
1597+
14701598
mutating func decode(_: Float.Type) throws -> Float {
14711599
try decodeFloatingPoint()
14721600
}
14731601

1602+
mutating func decodeIfPresent(_ type: Float.Type) throws -> Float? {
1603+
try decodeFloatingPointIfPresent()
1604+
}
1605+
14741606
mutating func decode(_: Int.Type) throws -> Int {
14751607
try decodeFixedWidthInteger()
14761608
}
14771609

1610+
mutating func decodeIfPresent(_: Int.Type) throws -> Int? {
1611+
try decodeFixedWidthIntegerIfPresent()
1612+
}
1613+
14781614
mutating func decode(_: Int8.Type) throws -> Int8 {
14791615
try decodeFixedWidthInteger()
14801616
}
14811617

1618+
mutating func decodeIfPresent(_: Int8.Type) throws -> Int8? {
1619+
try decodeFixedWidthIntegerIfPresent()
1620+
}
1621+
14821622
mutating func decode(_: Int16.Type) throws -> Int16 {
14831623
try decodeFixedWidthInteger()
14841624
}
14851625

1626+
mutating func decodeIfPresent(_: Int16.Type) throws -> Int16? {
1627+
try decodeFixedWidthIntegerIfPresent()
1628+
}
1629+
14861630
mutating func decode(_: Int32.Type) throws -> Int32 {
14871631
try decodeFixedWidthInteger()
14881632
}
14891633

1634+
mutating func decodeIfPresent(_: Int32.Type) throws -> Int32? {
1635+
try decodeFixedWidthIntegerIfPresent()
1636+
}
1637+
14901638
mutating func decode(_: Int64.Type) throws -> Int64 {
14911639
try decodeFixedWidthInteger()
14921640
}
@@ -1496,22 +1644,42 @@ extension JSONDecoderImpl {
14961644
try decodeFixedWidthInteger()
14971645
}
14981646

1647+
mutating func decodeIfPresent(_: Int64.Type) throws -> Int64? {
1648+
try decodeFixedWidthIntegerIfPresent()
1649+
}
1650+
14991651
mutating func decode(_: UInt.Type) throws -> UInt {
15001652
try decodeFixedWidthInteger()
15011653
}
15021654

1655+
mutating func decodeIfPresent(_: UInt.Type) throws -> UInt? {
1656+
try decodeFixedWidthIntegerIfPresent()
1657+
}
1658+
15031659
mutating func decode(_: UInt8.Type) throws -> UInt8 {
15041660
try decodeFixedWidthInteger()
15051661
}
15061662

1663+
mutating func decodeIfPresent(_: UInt8.Type) throws -> UInt8? {
1664+
try decodeFixedWidthIntegerIfPresent()
1665+
}
1666+
15071667
mutating func decode(_: UInt16.Type) throws -> UInt16 {
15081668
try decodeFixedWidthInteger()
15091669
}
15101670

1671+
mutating func decodeIfPresent(_: UInt16.Type) throws -> UInt16? {
1672+
try decodeFixedWidthIntegerIfPresent()
1673+
}
1674+
15111675
mutating func decode(_: UInt32.Type) throws -> UInt32 {
15121676
try decodeFixedWidthInteger()
15131677
}
15141678

1679+
mutating func decodeIfPresent(_: UInt32.Type) throws -> UInt32? {
1680+
try decodeFixedWidthIntegerIfPresent()
1681+
}
1682+
15151683
mutating func decode(_: UInt64.Type) throws -> UInt64 {
15161684
try decodeFixedWidthInteger()
15171685
}
@@ -1521,6 +1689,10 @@ extension JSONDecoderImpl {
15211689
try decodeFixedWidthInteger()
15221690
}
15231691

1692+
mutating func decodeIfPresent(_: UInt64.Type) throws -> UInt64? {
1693+
try decodeFixedWidthIntegerIfPresent()
1694+
}
1695+
15241696
mutating func decode<T: Decodable>(_ type: T.Type) throws -> T {
15251697
let value = try self.peekNextValue(ofType: type)
15261698
let result = try impl.unwrap(value, as: type, for: codingPathNode, currentIndexKey)
@@ -1529,6 +1701,16 @@ extension JSONDecoderImpl {
15291701
return result
15301702
}
15311703

1704+
mutating func decodeIfPresent<T: Decodable>(_ type: T.Type) throws -> T? {
1705+
let value = self.peekNextValueIfPresent(ofType: type)
1706+
let result: T? = switch value {
1707+
case nil, .null: nil
1708+
default: try impl.unwrap(value.unsafelyUnwrapped, as: type, for: codingPathNode, currentIndexKey)
1709+
}
1710+
advanceToNextValue()
1711+
return result
1712+
}
1713+
15321714
mutating func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> {
15331715
let value = try self.peekNextValue(ofType: KeyedDecodingContainer<NestedKey>.self)
15341716
let container = try impl.with(value: value, path: codingPathNode.appending(index: currentIndex)) {
@@ -1568,11 +1750,20 @@ extension JSONDecoderImpl {
15681750
}
15691751

15701752
@inline(__always)
1571-
private mutating func peekNextValue<T>(ofType type: T.Type) throws -> JSONMap.Value {
1753+
private mutating func peekNextValueIfPresent<T>(ofType type: T.Type) -> JSONMap.Value? {
15721754
if let value = peekedValue {
15731755
return value
15741756
}
15751757
guard let nextValue = valueIterator.next() else {
1758+
return nil
1759+
}
1760+
peekedValue = nextValue
1761+
return nextValue
1762+
}
1763+
1764+
@inline(__always)
1765+
private mutating func peekNextValue<T>(ofType type: T.Type) throws -> JSONMap.Value {
1766+
guard let nextValue = peekNextValueIfPresent(ofType: type) else {
15761767
var message = "Unkeyed container is at end."
15771768
if T.self == UnkeyedContainer.self {
15781769
message = "Cannot get nested unkeyed container -- unkeyed container is at end."
@@ -1590,7 +1781,6 @@ extension JSONDecoderImpl {
15901781
debugDescription: message,
15911782
underlyingError: nil))
15921783
}
1593-
peekedValue = nextValue
15941784
return nextValue
15951785
}
15961786

@@ -1609,6 +1799,26 @@ extension JSONDecoderImpl {
16091799
advanceToNextValue()
16101800
return result
16111801
}
1802+
1803+
@inline(__always) private mutating func decodeFixedWidthIntegerIfPresent<T: FixedWidthInteger>() throws -> T? {
1804+
let value = self.peekNextValueIfPresent(ofType: T.self)
1805+
let result: T? = switch value {
1806+
case nil, .null: nil
1807+
default: try impl.unwrapFixedWidthInteger(from: value.unsafelyUnwrapped, as: T.self, for: codingPathNode, currentIndexKey)
1808+
}
1809+
advanceToNextValue()
1810+
return result
1811+
}
1812+
1813+
@inline(__always) private mutating func decodeFloatingPointIfPresent<T: PrevalidatedJSONNumberBufferConvertible & BinaryFloatingPoint>() throws -> T? {
1814+
let value = self.peekNextValueIfPresent(ofType: T.self)
1815+
let result: T? = switch value {
1816+
case nil, .null: nil
1817+
default: try impl.unwrapFloatingPoint(from: value.unsafelyUnwrapped, as: T.self, for: codingPathNode, currentIndexKey)
1818+
}
1819+
advanceToNextValue()
1820+
return result
1821+
}
16121822
}
16131823
}
16141824

0 commit comments

Comments
 (0)