@@ -937,16 +937,32 @@ class ReerJSONTests: XCTestCase {
937937 let homeAddress : Address
938938 let workInfo : WorkInfo
939939
940+ // 添加更多可选属性用于测试 decodeIfPresent
941+ let middleName : String ?
942+ let alternateEmail : String ?
943+ let userScore : Int ?
944+ let isPremium : Bool ?
945+ let accountBalance : Double ?
946+ let lastLoginTime : Float ?
947+ let profilePicture : Data ?
948+ let websiteUrl : URL ?
949+ let birthDate : Date ?
950+
940951 struct Address : Codable , Equatable {
941952 let streetName : String
942953 let cityName : String
943954 let zipCode : String
955+ let apartmentNumber : String ? // 可选属性
956+ let buildingFloor : Int ? // 可选属性
944957 }
945958
946959 struct WorkInfo : Codable , Equatable {
947960 let companyName : String
948961 let jobTitle : String
949962 let startDate : String
963+ let endDate : String ? // 可选属性
964+ let monthlySalary : Double ? // 可选属性
965+ let isRemote : Bool ? // 可选属性
950966 }
951967 }
952968
@@ -961,17 +977,29 @@ class ReerJSONTests: XCTestCase {
961977 " home_address " : {
962978 " street_name " : " Main St " ,
963979 " city_name " : " New York " ,
964- " zip_code " : " 10001 "
980+ " zip_code " : " 10001 " ,
981+ " apartment_number " : " 4B " ,
982+ " building_floor " : null
965983 },
966984 " work_info " : {
967985 " company_name " : " Tech Corp " ,
968986 " job_title " : " Engineer " ,
969- " start_date " : " 2020-01-01 "
970- }
987+ " start_date " : " 2020-01-01 " ,
988+ " monthly_salary " : 8500.50,
989+ " is_remote " : true
990+ },
991+ " middle_name " : null,
992+ " user_score " : 95,
993+ " is_premium " : false,
994+ " account_balance " : 1250.75,
995+ " last_login_time " : 3.14159,
996+ " profile_picture " : " SGVsbG8gV29ybGQ= " ,
997+ " website_url " : " https://johndoe.dev " ,
998+ " birth_date " : 631152000
971999 }
9721000 """
9731001
974- testRoundTrip ( of: SnakeCaseTest . self, json: json, keyDecodingStrategy: . convertFromSnakeCase)
1002+ testRoundTrip ( of: SnakeCaseTest . self, json: json, dateDecodingStrategy : . secondsSince1970 , dataDecodingStrategy : . base64 , keyDecodingStrategy: . convertFromSnakeCase)
9751003 }
9761004
9771005 func testPreTransformKeyedDecodingContainerCustomStrategy( ) {
@@ -981,11 +1009,29 @@ class ReerJSONTests: XCTestCase {
9811009 let userAge : Int
9821010 let isAdmin : Bool
9831011
1012+ // 添加可选属性测试 decodeIfPresent
1013+ let userNickname : String ?
1014+ let userPhone : String ?
1015+ let userScore : Int ?
1016+ let isVerified : Bool ?
1017+ let userRating : Double ?
1018+ let lastSeen : Float ?
1019+ let avatarData : Data ?
1020+ let profileUrl : URL ?
1021+
9841022 enum CodingKeys : String , CodingKey {
9851023 case userName = " user_name "
9861024 case userEmail = " user_email "
9871025 case userAge = " user_age "
9881026 case isAdmin = " is_admin "
1027+ case userNickname = " user_nickname "
1028+ case userPhone = " user_phone "
1029+ case userScore = " user_score "
1030+ case isVerified = " is_verified "
1031+ case userRating = " user_rating "
1032+ case lastSeen = " last_seen "
1033+ case avatarData = " avatar_data "
1034+ case profileUrl = " profile_url "
9891035 }
9901036 }
9911037
@@ -994,7 +1040,14 @@ class ReerJSONTests: XCTestCase {
9941040 " USER_NAME " : " alice " ,
9951041 " USER_EMAIL " : " alice@example.com " ,
9961042 " USER_AGE " : 25,
997- " IS_ADMIN " : false
1043+ " IS_ADMIN " : false,
1044+ " USER_NICKNAME " : " Ali " ,
1045+ " USER_SCORE " : 88,
1046+ " IS_VERIFIED " : null,
1047+ " USER_RATING " : 4.7,
1048+ " LAST_SEEN " : 1.23,
1049+ " AVATAR_DATA " : " dGVzdCBkYXRh " ,
1050+ " PROFILE_URL " : " https://alice.dev "
9981051 }
9991052 """
10001053
@@ -1006,7 +1059,7 @@ class ReerJSONTests: XCTestCase {
10061059 return _CodingKey ( stringValue: lowerKey) !
10071060 }
10081061
1009- testRoundTrip ( of: CustomKeyTest . self, json: json, keyDecodingStrategy: customStrategy)
1062+ testRoundTrip ( of: CustomKeyTest . self, json: json, dataDecodingStrategy : . base64 , keyDecodingStrategy: customStrategy)
10101063 }
10111064
10121065 func testPreTransformKeyedDecodingContainerCustomStrategyWithPath( ) {
@@ -1359,19 +1412,39 @@ class ReerJSONTests: XCTestCase {
13591412 let floatValue : Float
13601413 let doubleValue : Double
13611414
1362- // Optional types
1415+ // Optional types for decodeIfPresent testing
13631416 let optionalString : String ?
13641417 let optionalInt : Int ?
13651418 let optionalBool : Bool ?
13661419
1420+ // Optional integer types
1421+ let optionalInt8 : Int8 ?
1422+ let optionalInt16 : Int16 ?
1423+ let optionalInt32 : Int32 ?
1424+ let optionalInt64 : Int64 ?
1425+ let optionalUInt : UInt ?
1426+ let optionalUInt8 : UInt8 ?
1427+ let optionalUInt16 : UInt16 ?
1428+ let optionalUInt32 : UInt32 ?
1429+ let optionalUInt64 : UInt64 ?
1430+
1431+ // Optional floating point types
1432+ let optionalFloat : Float ?
1433+ let optionalDouble : Double ?
1434+
13671435 // Collections
13681436 let arrayValue : [ String ]
13691437 let dictionaryValue : [ String : Int ]
1438+ let optionalArrayValue : [ Int ] ?
1439+ let optionalDictionaryValue : [ String : String ] ?
13701440
13711441 // Special types
13721442 let urlValue : URL
13731443 let dateValue : Date
13741444 let dataValue : Data
1445+ let optionalUrlValue : URL ?
1446+ let optionalDateValue : Date ?
1447+ let optionalDataValue : Data ?
13751448 }
13761449
13771450 let json = """
@@ -1393,14 +1466,30 @@ class ReerJSONTests: XCTestCase {
13931466 " optional_string " : " optional_test " ,
13941467 " optional_int " : 123,
13951468 " optional_bool " : null,
1469+ " optional_int8 " : 100,
1470+ " optional_int16 " : null,
1471+ " optional_int32 " : 999999,
1472+ " optional_int64 " : 1234567890123456789,
1473+ " optional_u_int " : null,
1474+ " optional_u_int8 " : 200,
1475+ " optional_u_int16 " : 50000,
1476+ " optional_u_int32 " : null,
1477+ " optional_u_int64 " : 9876543210987654321,
1478+ " optional_float " : 2.718,
1479+ " optional_double " : null,
13961480 " array_value " : [ " item1 " , " item2 " , " item3 " ],
13971481 " dictionary_value " : {
13981482 " key1 " : 1,
13991483 " key2 " : 2
14001484 },
1485+ " optional_array_value " : [10, 20, 30],
1486+ " optional_dictionary_value " : null,
14011487 " url_value " : " https://example.com " ,
14021488 " date_value " : 1609459200,
1403- " data_value " : " SGVsbG8gV29ybGQ= "
1489+ " data_value " : " SGVsbG8gV29ybGQ= " ,
1490+ " optional_url_value " : " https://optional.example.com " ,
1491+ " optional_date_value " : null,
1492+ " optional_data_value " : " T3B0aW9uYWwgRGF0YQ== "
14041493 }
14051494 """
14061495
@@ -1499,6 +1588,35 @@ class ReerJSONTests: XCTestCase {
14991588 XCTAssertEqual ( result. urlValue, URL ( string: " https://example.com " ) !)
15001589 XCTAssertEqual ( result. dateValue, Date ( timeIntervalSince1970: 1609459200 ) )
15011590 XCTAssertEqual ( result. dataValue, " Hello World " . data ( using: . utf8) !)
1591+
1592+ // Verify optional values from decodeIfPresent
1593+ XCTAssertEqual ( result. optionalString, " optional_test " )
1594+ XCTAssertEqual ( result. optionalInt, 123 )
1595+ XCTAssertNil ( result. optionalBool)
1596+
1597+ // Verify optional integer types
1598+ XCTAssertEqual ( result. optionalInt8, 100 )
1599+ XCTAssertNil ( result. optionalInt16)
1600+ XCTAssertEqual ( result. optionalInt32, 999999 )
1601+ XCTAssertEqual ( result. optionalInt64, 1234567890123456789 )
1602+ XCTAssertNil ( result. optionalUInt)
1603+ XCTAssertEqual ( result. optionalUInt8, 200 )
1604+ XCTAssertEqual ( result. optionalUInt16, 50000 )
1605+ XCTAssertNil ( result. optionalUInt32)
1606+ XCTAssertEqual ( result. optionalUInt64, 9876543210987654321 )
1607+
1608+ // Verify optional floating point types
1609+ XCTAssertEqual ( result. optionalFloat!, 2.718 , accuracy: 0.001 )
1610+ XCTAssertNil ( result. optionalDouble)
1611+
1612+ // Verify optional collections
1613+ XCTAssertEqual ( result. optionalArrayValue, [ 10 , 20 , 30 ] )
1614+ XCTAssertNil ( result. optionalDictionaryValue)
1615+
1616+ // Verify optional special types
1617+ XCTAssertEqual ( result. optionalUrlValue, URL ( string: " https://optional.example.com " ) !)
1618+ XCTAssertNil ( result. optionalDateValue)
1619+ XCTAssertEqual ( result. optionalDataValue, " Optional Data " . data ( using: . utf8) !)
15021620 }
15031621
15041622 func testPreTransformKeyedDecodingContainerDecodeIfPresent( ) {
@@ -1538,6 +1656,159 @@ class ReerJSONTests: XCTestCase {
15381656 XCTAssertNil ( result. nullInt)
15391657 }
15401658
1659+ func testPreTransformKeyedDecodingContainerDecodeIfPresentEdgeCases( ) {
1660+ struct EdgeCaseTest : Codable , Equatable {
1661+ // 测试各种整数类型的 decodeIfPresent
1662+ let optInt8Present : Int8 ?
1663+ let optInt8Null : Int8 ?
1664+ let optInt8Missing : Int8 ?
1665+
1666+ let optInt16Present : Int16 ?
1667+ let optInt16Null : Int16 ?
1668+ let optInt16Missing : Int16 ?
1669+
1670+ let optInt32Present : Int32 ?
1671+ let optInt32Null : Int32 ?
1672+ let optInt32Missing : Int32 ?
1673+
1674+ let optInt64Present : Int64 ?
1675+ let optInt64Null : Int64 ?
1676+ let optInt64Missing : Int64 ?
1677+
1678+ let optUIntPresent : UInt ?
1679+ let optUIntNull : UInt ?
1680+ let optUIntMissing : UInt ?
1681+
1682+ let optUInt8Present : UInt8 ?
1683+ let optUInt8Null : UInt8 ?
1684+ let optUInt8Missing : UInt8 ?
1685+
1686+ let optUInt16Present : UInt16 ?
1687+ let optUInt16Null : UInt16 ?
1688+ let optUInt16Missing : UInt16 ?
1689+
1690+ let optUInt32Present : UInt32 ?
1691+ let optUInt32Null : UInt32 ?
1692+ let optUInt32Missing : UInt32 ?
1693+
1694+ let optUInt64Present : UInt64 ?
1695+ let optUInt64Null : UInt64 ?
1696+ let optUInt64Missing : UInt64 ?
1697+
1698+ // 测试浮点类型的 decodeIfPresent
1699+ let optFloatPresent : Float ?
1700+ let optFloatNull : Float ?
1701+ let optFloatMissing : Float ?
1702+
1703+ let optDoublePresent : Double ?
1704+ let optDoubleNull : Double ?
1705+ let optDoubleMissing : Double ?
1706+
1707+ // 测试布尔类型的 decodeIfPresent
1708+ let optBoolPresent : Bool ?
1709+ let optBoolNull : Bool ?
1710+ let optBoolMissing : Bool ?
1711+
1712+ // 测试字符串类型的 decodeIfPresent
1713+ let optStringPresent : String ?
1714+ let optStringNull : String ?
1715+ let optStringMissing : String ?
1716+ }
1717+
1718+ let json = """
1719+ {
1720+ " opt_int8_present " : 127,
1721+ " opt_int8_null " : null,
1722+
1723+ " opt_int16_present " : 32767,
1724+ " opt_int16_null " : null,
1725+
1726+ " opt_int32_present " : 2147483647,
1727+ " opt_int32_null " : null,
1728+
1729+ " opt_int64_present " : 9223372036854775807,
1730+ " opt_int64_null " : null,
1731+
1732+ " opt_u_int_present " : 4294967295,
1733+ " opt_u_int_null " : null,
1734+
1735+ " opt_u_int8_present " : 255,
1736+ " opt_u_int8_null " : null,
1737+
1738+ " opt_u_int16_present " : 65535,
1739+ " opt_u_int16_null " : null,
1740+
1741+ " opt_u_int32_present " : 4294967295,
1742+ " opt_u_int32_null " : null,
1743+
1744+ " opt_u_int64_present " : 18446744073709551615,
1745+ " opt_u_int64_null " : null,
1746+
1747+ " opt_float_present " : 3.14159,
1748+ " opt_float_null " : null,
1749+
1750+ " opt_double_present " : 2.71828182846,
1751+ " opt_double_null " : null,
1752+
1753+ " opt_bool_present " : true,
1754+ " opt_bool_null " : null,
1755+
1756+ " opt_string_present " : " hello world " ,
1757+ " opt_string_null " : null
1758+ }
1759+ """
1760+
1761+ let decoder = ReerJSONDecoder ( )
1762+ decoder. keyDecodingStrategy = . convertFromSnakeCase
1763+
1764+ let result = try ! decoder. decode ( EdgeCaseTest . self, from: json. data ( using: . utf8) !)
1765+
1766+ // 验证 present 值
1767+ XCTAssertEqual ( result. optInt8Present, 127 )
1768+ XCTAssertEqual ( result. optInt16Present, 32767 )
1769+ XCTAssertEqual ( result. optInt32Present, 2147483647 )
1770+ XCTAssertEqual ( result. optInt64Present, 9223372036854775807 )
1771+ XCTAssertEqual ( result. optUIntPresent, 4294967295 )
1772+ XCTAssertEqual ( result. optUInt8Present, 255 )
1773+ XCTAssertEqual ( result. optUInt16Present, 65535 )
1774+ XCTAssertEqual ( result. optUInt32Present, 4294967295 )
1775+ XCTAssertEqual ( result. optUInt64Present, 18446744073709551615 )
1776+ XCTAssertEqual ( result. optFloatPresent!, 3.14159 , accuracy: 0.00001 )
1777+ XCTAssertEqual ( result. optDoublePresent!, 2.71828182846 , accuracy: 0.00000000001 )
1778+ XCTAssertEqual ( result. optBoolPresent, true )
1779+ XCTAssertEqual ( result. optStringPresent, " hello world " )
1780+
1781+ // 验证 null 值
1782+ XCTAssertNil ( result. optInt8Null)
1783+ XCTAssertNil ( result. optInt16Null)
1784+ XCTAssertNil ( result. optInt32Null)
1785+ XCTAssertNil ( result. optInt64Null)
1786+ XCTAssertNil ( result. optUIntNull)
1787+ XCTAssertNil ( result. optUInt8Null)
1788+ XCTAssertNil ( result. optUInt16Null)
1789+ XCTAssertNil ( result. optUInt32Null)
1790+ XCTAssertNil ( result. optUInt64Null)
1791+ XCTAssertNil ( result. optFloatNull)
1792+ XCTAssertNil ( result. optDoubleNull)
1793+ XCTAssertNil ( result. optBoolNull)
1794+ XCTAssertNil ( result. optStringNull)
1795+
1796+ // 验证 missing 值
1797+ XCTAssertNil ( result. optInt8Missing)
1798+ XCTAssertNil ( result. optInt16Missing)
1799+ XCTAssertNil ( result. optInt32Missing)
1800+ XCTAssertNil ( result. optInt64Missing)
1801+ XCTAssertNil ( result. optUIntMissing)
1802+ XCTAssertNil ( result. optUInt8Missing)
1803+ XCTAssertNil ( result. optUInt16Missing)
1804+ XCTAssertNil ( result. optUInt32Missing)
1805+ XCTAssertNil ( result. optUInt64Missing)
1806+ XCTAssertNil ( result. optFloatMissing)
1807+ XCTAssertNil ( result. optDoubleMissing)
1808+ XCTAssertNil ( result. optBoolMissing)
1809+ XCTAssertNil ( result. optStringMissing)
1810+ }
1811+
15411812 func testPreTransformKeyedDecodingContainerContains( ) {
15421813 struct ContainsTest : Codable , Equatable {
15431814 let value : String
0 commit comments