diff --git a/.gitignore b/.gitignore index 4f20167..325db43 100644 --- a/.gitignore +++ b/.gitignore @@ -89,3 +89,22 @@ fastlane/test_output iOSInjectionProject/ .DS_Store + + +# Swift Package Manager +/.build +/Packages +Package.resolved + +# 如果你用 Xcode 打开 Package.swift,会生成 .swiftpm +.swiftpm/xcode + +# Playground 支持 +timeline.xctimeline +playground.xcworkspace + +# SwiftPM artifact cache (Swift 5.8+,如果用到了 binary target) +.artifacts + +# Xcode Preview snapshots +*.preview-thumbnails diff --git a/Sources/SmartCodable/Core/JSONEncoder/EncodingCache.swift b/Sources/SmartCodable/Core/JSONEncoder/EncodingCache.swift index 6e9b564..4bf2ece 100644 --- a/Sources/SmartCodable/Core/JSONEncoder/EncodingCache.swift +++ b/Sources/SmartCodable/Core/JSONEncoder/EncodingCache.swift @@ -20,6 +20,7 @@ class EncodingCache: Cachable { var snapshot = EncodingSnapshot() snapshot.objectType = object + snapshot.codingPath = codingPath snapshot.transformers = object.mappingForValue() snapshots.append(snapshot) } diff --git a/Sources/SmartCodable/Core/JSONEncoder/Impl/_SpecialTreatmentEncoder.swift b/Sources/SmartCodable/Core/JSONEncoder/Impl/_SpecialTreatmentEncoder.swift index 4b64ed4..f5f8d1a 100644 --- a/Sources/SmartCodable/Core/JSONEncoder/Impl/_SpecialTreatmentEncoder.swift +++ b/Sources/SmartCodable/Core/JSONEncoder/Impl/_SpecialTreatmentEncoder.swift @@ -67,11 +67,10 @@ extension _SpecialTreatmentEncoder { return try self.wrapObject(object as! [String: Encodable], for: additionalKey) default: - impl.cache.cacheSnapshot(for: E.self, codingPath: codingPath) - let encoder = self.getEncoder(for: additionalKey) + encoder.cache.cacheSnapshot(for: E.self, codingPath: encoder.codingPath) try encodable.encode(to: encoder) - impl.cache.removeSnapshot(for: E.self) + encoder.cache.removeSnapshot(for: E.self) // If it is modified by SmartFlat, you need to encode to the upper layer to restore the data. if encodable is FlatType { diff --git a/Tests/Example.swift b/Tests/Example.swift index 56bae76..ef50020 100644 --- a/Tests/Example.swift +++ b/Tests/Example.swift @@ -1,4 +1,6 @@ import XCTest +import SmartCodableInherit +import Foundation @testable import SmartCodable @@ -33,8 +35,36 @@ class Tests: XCTestCase { } + + func testJSONEncode() { + var dic: [String: Any] = [ + "id": 563, + "owner_id": 264, + "title": "langwang004+82 ワークスペース", + "icon": "", + "type": 2, + "used_seat": 1, + "created_at": "2025-07-25T02:58:35Z", + ] + + let subscription: [String: Any] = [ + "cancel_at_period_end": true, + "current_period_end_at": "2025-07-30T03:37:03Z", + "price_id": "personal_plan_annual_trial", + "status": "past_due" + ] + dic["subscription"] = subscription as any Equatable + + let model = WorkspaceModel.deserialize(from: dic) + XCTAssertNotNil(model) + let modelDic = model?.toDictionary(useMappedKeys: true) + XCTAssertNotNil(modelDic) + let res = deepEqualDict(dic, modelDic!) + XCTAssertTrue(res) + } } + // SmartCodable struct Smart: SmartCodable { var name: String? @@ -66,3 +96,94 @@ class SubModel: BaseModel { return "我的名字是\(self.name)" }() } + +// MARK: - Test JSON Encode +func deepEqual(_ lhs: Any, _ rhs: Any) -> Bool { + switch (lhs, rhs) { + case let (a as Int, b as Int): return a == b + case let (a as Double, b as Double): return a == b + case let (a as String, b as String): return a == b + case let (a as Bool, b as Bool): return a == b + case let (a as [String: Any], b as [String: Any]): + return deepEqualDict(a, b) + case let (a as [Any], b as [Any]): + return deepEqualArray(a, b) + case let (a as NSNumber, b as NSNumber): + return a == b + case (is NSNull, is NSNull): + return true + default: + return false + } +} + +func deepEqualDict(_ lhs: [String: Any], _ rhs: [String: Any]) -> Bool { + guard lhs.count == rhs.count else { return false } + for (key, lValue) in lhs { + print("compare key: \(key), lValue: \(lValue)") + guard let rValue = rhs[key] else { + print("not found rValue for key: \(key), in rhs: \(rhs)") + return false + } + if !deepEqual(lValue, rValue) { + print("lValue: \(lValue) not equal to rValue: \(rValue)") + return false + } + } + print("key value equal in both") + return true +} + +func deepEqualArray(_ lhs: [Any], _ rhs: [Any]) -> Bool { + guard lhs.count == rhs.count else { return false } + for (l, r) in zip(lhs, rhs) { + if !deepEqual(l, r) { + return false + } + } + return true +} + +open class SCRootModel { + + required public init() { } + + open func didFinishMapping() { + + } + + open class func mappingForKey() -> [SmartKeyTransformer]? { + nil + } + + open class func mappingForValue() -> [SmartValueTransformer]? { + nil + } +} + + +class WorkspaceModel: SCRootModel, SmartCodable { + var id: Int = 0 + var owner_id: Int = 0 + var title: String = "" + var icon: String = "" + var type: Int = 0 + var used_seat: Int = 0 + var created_at: String = "" + var subscription: WorkspaceSubscription? +} + +class WorkspaceSubscription: SCRootModel, SmartCodable { + var cancelAtPeriodEnd: Bool = false + var currentPeriodEndAt: String = "" + var priceid: String = "" + var status: String = "" + + override class func mappingForKey() -> [SmartKeyTransformer]? { + [ + CodingKeys.cancelAtPeriodEnd <--- "cancel_at_period_end", + CodingKeys.currentPeriodEndAt <--- "current_period_end_at", + CodingKeys.priceid <--- "price_id", + ] + } +}