├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── DevGuideline.md
├── KakaJSON.podspec
├── KakaJSON.xcodeproj
├── KakaJSONTests_Info.plist
├── KakaJSON_Info.plist
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── xcshareddata
│ └── xcschemes
│ └── KakaJSON-Package.xcscheme
├── KakaJSON.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ └── WorkspaceSettings.xcsettings
├── KakaJSONDemo
├── KakaJSONDemo.xcodeproj
│ ├── project.pbxproj
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── KakaJSONDemo.xcscheme
└── KakaJSONDemo
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ └── ViewController.swift
├── LICENSE
├── Package.swift
├── README.md
├── Sources
└── KakaJSON
│ ├── Convert
│ ├── Convertible.swift
│ ├── ConvertibleConfig.swift
│ ├── ConvertibleEnum.swift
│ ├── KJ.swift
│ ├── Logger.swift
│ ├── TypeProxy.swift
│ └── Values.swift
│ ├── Extension
│ ├── Array+KJ.swift
│ ├── Data+KJ.swift
│ ├── Dictionary+KJ.swift
│ ├── JSONSerialization+KJ.swift
│ ├── NSObject+KJ.swift
│ ├── Optional+KJ.swift
│ ├── Pointer+KJ.swift
│ └── String+KJ.swift
│ ├── Global
│ ├── Coding.swift
│ ├── JSON.swift
│ └── Model.swift
│ └── Metadata
│ ├── Descriptor
│ ├── ClassDescriptor.swift
│ ├── Descriptor.swift
│ ├── EnumDescriptor.swift
│ ├── FieldDescriptor.swift
│ └── StructDescriptor.swift
│ ├── LICENSE
│ ├── Layout
│ ├── BaseLayout.swift
│ ├── ClassLayout.swift
│ ├── EnumLayout.swift
│ ├── FieldList.swift
│ ├── FunctionLayout.swift
│ ├── Layout.swift
│ ├── ProtocolLayout.swift
│ ├── StructLayout.swift
│ └── TupleLayout.swift
│ ├── Metadata.swift
│ ├── Property.swift
│ └── Type
│ ├── BaseType.swift
│ ├── ClassType.swift
│ ├── EnumType.swift
│ ├── ForeignClassType.swift
│ ├── FunctionType.swift
│ ├── Kind.swift
│ ├── MetaType.swift
│ ├── ModelType.swift
│ ├── ObjCClassType.swift
│ ├── OptionalType.swift
│ ├── ProtocolType.swift
│ ├── StructType.swift
│ ├── TupleType.swift
│ └── Type.swift
└── Tests
├── KakaJSONTests
├── JSON_To_Model
│ ├── JTM_01_Basic.swift
│ ├── JTM_02_DataType.swift
│ ├── JTM_03_NestedModel.swift
│ ├── JTM_04_ModelArray.swift
│ ├── JTM_05_KeyMapping.swift
│ ├── JTM_06_CustomValue.swift
│ └── JTM_07_DynamicModel.swift
├── Model_To_JSON
│ ├── MTJ_01_Basic.swift
│ ├── MTJ_02_NestedModel.swift
│ ├── MTJ_03_ModelArray.swift
│ ├── MTJ_04_KeyMapping.swift
│ └── MTJ_05_CustomValue.swift
├── OC
│ ├── KakaJSONTests-Bridging-Header.h
│ ├── OCModel.h
│ └── OCModel.m
├── Other
│ ├── Bugs.swift
│ ├── Coding.swift
│ └── Global.swift
└── XCTestManifests.swift
└── LinuxMain.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | # Package.resolved
41 | .build/
42 |
43 | # CocoaPods
44 | #
45 | # We recommend against adding the Pods directory to your .gitignore. However
46 | # you should judge for yourself, the pros and cons are mentioned at:
47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48 | #
49 | # Pods/
50 |
51 | # Carthage
52 | #
53 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
54 | # Carthage/Checkouts
55 |
56 | Carthage/Build
57 |
58 | # fastlane
59 | #
60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
61 | # screenshots whenever they are needed.
62 | # For more information about the recommended setup visit:
63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
64 |
65 | fastlane/report.xml
66 | fastlane/Preview.html
67 | fastlane/screenshots/**/*.png
68 | fastlane/test_output
69 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | env:
2 | global:
3 | - PROJECT="KakaJSON.xcodeproj"
4 | - SCHEME="KakaJSON-Package"
5 | - SWIFT_VERSION=5.0
6 |
7 | matrix:
8 | include:
9 | - os: osx
10 | language: swift
11 | osx_image: xcode10.2
12 | env:
13 | - SDK="iphonesimulator12.2"
14 | - DESTINATION="platform=iOS Simulator,name=iPhone 8,OS=12.2"
15 | - os: osx
16 | language: swift
17 | osx_image: xcode10.2
18 | env:
19 | - SDK="macosx10.14"
20 | - DESTINATION="arch=x86_64"
21 | - os: osx
22 | language: swift
23 | osx_image: xcode10.2
24 | env:
25 | - SDK="appletvsimulator12.0"
26 | - DESTINATION="OS=12.0,name=Apple TV 4K"
27 | # - os: linux
28 | # language: generic
29 | # dist: trusty
30 | # sudo: required
31 |
32 | before_install:
33 | - echo $TRAVIS_OS_NAME
34 | - if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
35 | gem install xcpretty;
36 | fi
37 | - if [[ $TRAVIS_OS_NAME == 'linux' ]]; then
38 | eval "$(curl -sL https://swiftenv.fuller.li/install.sh)";
39 | fi
40 |
41 | script:
42 | - if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
43 | xcodebuild clean build test -project "$PROJECT" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -enableCodeCoverage YES | xcpretty;
44 | fi
45 | - if [[ $TRAVIS_OS_NAME == 'linux' ]]; then
46 | swift test;
47 | fi
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ---
4 |
5 | ## [1.1.2](https://github.com/kakaopensource/KakaJSON/releases/tag/1.1.2) (2019-12-23)
6 | - Supporting convert from [String: [[String: Any]]] to [String: [Model]]
7 | - Supporting 'enum array', 'enum dict'
8 | - Closed issues
9 | - [嵌套解析出错](https://github.com/kakaopensource/KakaJSON/issues/39)
10 |
11 | ## [1.1.1](https://github.com/kakaopensource/KakaJSON/releases/tag/1.1.1) (2019-11-04)
12 | - Adjust Lock
13 | - Closed issues
14 | - [iOS 9 Crash](https://github.com/kakaopensource/KakaJSON/issues/31)
15 | - Merged pull requests
16 | - [use isUppercase to check if the character is uppercase](https://github.com/kakaopensource/KakaJSON/pull/20)
17 | - [A key should be able to contain dot](https://github.com/kakaopensource/KakaJSON/pull/21)
18 | - [use enum as namespace, as Apple does in Combine](https://github.com/kakaopensource/KakaJSON/pull/23)
19 | - [return early if possible](https://github.com/kakaopensource/KakaJSON/pull/24)
20 | - [add test testComplex_JSONKeyWithDot_bestMatch](https://github.com/kakaopensource/KakaJSON/pull/25)
21 | - [[big pr]: refactor project structure](https://github.com/kakaopensource/KakaJSON/pull/27)
22 |
23 |
24 |
25 | ## [1.1.0](https://github.com/kakaopensource/KakaJSON/releases/tag/1.1.0) (2019-08-28)
26 | - Remove locks in ConvertibleConfig
27 | - Simplify apis
28 | - Support carthage
29 | - Keep default model value for optimization
30 | - Merged pull requests
31 | - [simplify optional.kj_value](https://github.com/kakaopensource/KakaJSON/pull/16)
32 | - [replace symbol ~= with pattern keyword](https://github.com/kakaopensource/KakaJSON/pull/17)
33 |
34 |
35 |
36 | ## [1.0.0](https://github.com/kakaopensource/KakaJSON/releases/tag/1.0.0) (2019-08-23)
37 |
38 | - First public release
39 |
--------------------------------------------------------------------------------
/DevGuideline.md:
--------------------------------------------------------------------------------
1 |
2 | ## KakaJSON DevGuideline
3 |
4 | #### Metadata Reference:
5 |
6 | - [https://github.com/apple/swift/blob/master/docs/ABI/TypeMetadata.rst](https://github.com/apple/swift/blob/master/docs/ABI/TypeMetadata.rst)
7 | - [https://github.com/apple/swift/blob/master/include/swift/ABI/MetadataKind.def](https://github.com/apple/swift/blob/master/include/swift/ABI/MetadataKind.def)
8 | - [https://github.com/apple/swift/blob/master/include/swift/ABI/Metadata.h](https://github.com/apple/swift/blob/master/include/swift/ABI/Metadata.h)
9 | - [https://github.com/apple/swift/blob/master/include/swift/ABI/MetadataValues.h](https://github.com/apple/swift/blob/master/include/swift/ABI/MetadataValues.h)
10 | - [https://github.com/apple/swift/blob/master/utils/dtrace/runtime.d](https://github.com/apple/swift/blob/master/utils/dtrace/runtime.d)
11 | - [https://github.com/apple/swift/blob/master/include/swift/Reflection/Records.h](https://github.com/apple/swift/blob/master/include/swift/Reflection/Records.h)
12 |
13 | #### 通过测试用例
14 |
15 | 提交代码之前,请务必先保证在真机、模拟器上通过所有的测试用例(Debug+Release模式)
16 |
17 |
18 | #### 命名规范
19 |
20 | ① 给模型类型、系统自带类型,扩展public的成员
21 | - obj.kj.成员名称
22 | - 比如 "123".kj.numberCount, model.kj.JSON()
23 |
24 | ② 给模型类型、系统自带类型,扩展internal的成员
25 | - obj.kj_成员名称
26 | - 比如 "123".kj_numberCount, model.kj_JSON()
27 |
28 | ③ private\fileprivate的成员
29 | - obj._成员名称
30 | - 比如 "123"._numberCount, model._JSON()
31 |
32 | ④ 类型名、方法名必须是大写的`JSON`
33 | - 不要写`json`, 也不要写`Json`
34 | - 如果是变量名、常量名、参数名,可以用小写`json`
35 |
36 | ----------------------------------------------------------------------------
37 |
38 | #### TestPass
39 |
40 | Please make all test cases right before pushing your code
41 | - include iOS Device\Simulator
42 | - include release\debug mode
43 |
44 | #### NamingConvention
45 |
46 | ① public extension for system types、model types
47 | - obj.kj.memberName
48 | - e.g. "123".kj.numberCount, model.kj.JSON()
49 |
50 | ② internal extension for system types、model types
51 | - obj.kj_memberName
52 | - e.g. "123".kj_numberCount, model.kj_JSON()
53 |
54 | ③ private\fileprivate
55 | - obj._memberName
56 | - e.g. "123"._numberCount, model._JSON()
57 |
58 | ④ Name of method or type must be `JSON`
59 | - not `json`, not `Json`
60 | - name of variable can be `json`
61 |
--------------------------------------------------------------------------------
/KakaJSON.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "KakaJSON"
3 | s.version = "1.1.2"
4 | s.summary = "Fast conversion between JSON and model in Swift"
5 | s.homepage = "https://github.com/kakaopensource/KakaJSON"
6 | s.license = { :type => "MIT" }
7 | s.authors = { "MJ Lee" => "richermj123go@vip.qq.com" }
8 |
9 | s.requires_arc = true
10 | s.swift_version = "5.0"
11 |
12 | s.osx.deployment_target = "10.9"
13 | s.ios.deployment_target = "8.0"
14 | s.watchos.deployment_target = "2.0"
15 | s.tvos.deployment_target = "9.0"
16 |
17 | s.source = { :git => "https://github.com/kakaopensource/KakaJSON.git", :tag => s.version }
18 | s.source_files = "Sources/KakaJSON/**/*.swift"
19 | end
--------------------------------------------------------------------------------
/KakaJSON.xcodeproj/KakaJSONTests_Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CFBundleDevelopmentRegion
5 | en
6 | CFBundleExecutable
7 | $(EXECUTABLE_NAME)
8 | CFBundleIdentifier
9 | $(PRODUCT_BUNDLE_IDENTIFIER)
10 | CFBundleInfoDictionaryVersion
11 | 6.0
12 | CFBundleName
13 | $(PRODUCT_NAME)
14 | CFBundlePackageType
15 | BNDL
16 | CFBundleShortVersionString
17 | 1.0
18 | CFBundleSignature
19 | ????
20 | CFBundleVersion
21 | $(CURRENT_PROJECT_VERSION)
22 | NSPrincipalClass
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/KakaJSON.xcodeproj/KakaJSON_Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CFBundleDevelopmentRegion
5 | en
6 | CFBundleExecutable
7 | $(EXECUTABLE_NAME)
8 | CFBundleIdentifier
9 | $(PRODUCT_BUNDLE_IDENTIFIER)
10 | CFBundleInfoDictionaryVersion
11 | 6.0
12 | CFBundleName
13 | $(PRODUCT_NAME)
14 | CFBundlePackageType
15 | FMWK
16 | CFBundleShortVersionString
17 | 1.0
18 | CFBundleSignature
19 | ????
20 | CFBundleVersion
21 | $(CURRENT_PROJECT_VERSION)
22 | NSPrincipalClass
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/KakaJSON.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
--------------------------------------------------------------------------------
/KakaJSON.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/KakaJSON.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
6 |
7 |
8 |
--------------------------------------------------------------------------------
/KakaJSON.xcodeproj/xcshareddata/xcschemes/KakaJSON-Package.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
55 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
74 |
76 |
77 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/KakaJSON.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/KakaJSON.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/KakaJSON.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
6 |
7 |
8 |
--------------------------------------------------------------------------------
/KakaJSONDemo/KakaJSONDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 627B5EAD2321063E0046E23D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 627B5EAC2321063E0046E23D /* AppDelegate.swift */; };
11 | 627B5EAF2321063E0046E23D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 627B5EAE2321063E0046E23D /* ViewController.swift */; };
12 | 627B5EB22321063E0046E23D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 627B5EB02321063E0046E23D /* Main.storyboard */; };
13 | 627B5EB4232106400046E23D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 627B5EB3232106400046E23D /* Assets.xcassets */; };
14 | 627B5EB7232106400046E23D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 627B5EB5232106400046E23D /* LaunchScreen.storyboard */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXCopyFilesBuildPhase section */
18 | 627B5EC12321064A0046E23D /* Embed Frameworks */ = {
19 | isa = PBXCopyFilesBuildPhase;
20 | buildActionMask = 2147483647;
21 | dstPath = "";
22 | dstSubfolderSpec = 10;
23 | files = (
24 | );
25 | name = "Embed Frameworks";
26 | runOnlyForDeploymentPostprocessing = 0;
27 | };
28 | /* End PBXCopyFilesBuildPhase section */
29 |
30 | /* Begin PBXFileReference section */
31 | 627B5EA92321063E0046E23D /* KakaJSONDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KakaJSONDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
32 | 627B5EAC2321063E0046E23D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
33 | 627B5EAE2321063E0046E23D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
34 | 627B5EB12321063E0046E23D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
35 | 627B5EB3232106400046E23D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
36 | 627B5EB6232106400046E23D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
37 | 627B5EB8232106400046E23D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
38 | /* End PBXFileReference section */
39 |
40 | /* Begin PBXFrameworksBuildPhase section */
41 | 627B5EA62321063E0046E23D /* Frameworks */ = {
42 | isa = PBXFrameworksBuildPhase;
43 | buildActionMask = 2147483647;
44 | files = (
45 | );
46 | runOnlyForDeploymentPostprocessing = 0;
47 | };
48 | /* End PBXFrameworksBuildPhase section */
49 |
50 | /* Begin PBXGroup section */
51 | 627B5EA02321063E0046E23D = {
52 | isa = PBXGroup;
53 | children = (
54 | 627B5EAB2321063E0046E23D /* KakaJSONDemo */,
55 | 627B5EAA2321063E0046E23D /* Products */,
56 | );
57 | sourceTree = "";
58 | };
59 | 627B5EAA2321063E0046E23D /* Products */ = {
60 | isa = PBXGroup;
61 | children = (
62 | 627B5EA92321063E0046E23D /* KakaJSONDemo.app */,
63 | );
64 | name = Products;
65 | sourceTree = "";
66 | };
67 | 627B5EAB2321063E0046E23D /* KakaJSONDemo */ = {
68 | isa = PBXGroup;
69 | children = (
70 | 627B5EAC2321063E0046E23D /* AppDelegate.swift */,
71 | 627B5EAE2321063E0046E23D /* ViewController.swift */,
72 | 627B5EB02321063E0046E23D /* Main.storyboard */,
73 | 627B5EB3232106400046E23D /* Assets.xcassets */,
74 | 627B5EB5232106400046E23D /* LaunchScreen.storyboard */,
75 | 627B5EB8232106400046E23D /* Info.plist */,
76 | );
77 | path = KakaJSONDemo;
78 | sourceTree = "";
79 | };
80 | /* End PBXGroup section */
81 |
82 | /* Begin PBXNativeTarget section */
83 | 627B5EA82321063E0046E23D /* KakaJSONDemo */ = {
84 | isa = PBXNativeTarget;
85 | buildConfigurationList = 627B5EBB232106400046E23D /* Build configuration list for PBXNativeTarget "KakaJSONDemo" */;
86 | buildPhases = (
87 | 627B5EA52321063E0046E23D /* Sources */,
88 | 627B5EA62321063E0046E23D /* Frameworks */,
89 | 627B5EA72321063E0046E23D /* Resources */,
90 | 627B5EC12321064A0046E23D /* Embed Frameworks */,
91 | );
92 | buildRules = (
93 | );
94 | dependencies = (
95 | );
96 | name = KakaJSONDemo;
97 | productName = KakaJSONDemo;
98 | productReference = 627B5EA92321063E0046E23D /* KakaJSONDemo.app */;
99 | productType = "com.apple.product-type.application";
100 | };
101 | /* End PBXNativeTarget section */
102 |
103 | /* Begin PBXProject section */
104 | 627B5EA12321063E0046E23D /* Project object */ = {
105 | isa = PBXProject;
106 | attributes = {
107 | LastSwiftUpdateCheck = 1020;
108 | LastUpgradeCheck = 1020;
109 | ORGANIZATIONNAME = KakaJSON;
110 | TargetAttributes = {
111 | 627B5EA82321063E0046E23D = {
112 | CreatedOnToolsVersion = 10.2;
113 | };
114 | };
115 | };
116 | buildConfigurationList = 627B5EA42321063E0046E23D /* Build configuration list for PBXProject "KakaJSONDemo" */;
117 | compatibilityVersion = "Xcode 9.3";
118 | developmentRegion = en;
119 | hasScannedForEncodings = 0;
120 | knownRegions = (
121 | en,
122 | Base,
123 | );
124 | mainGroup = 627B5EA02321063E0046E23D;
125 | productRefGroup = 627B5EAA2321063E0046E23D /* Products */;
126 | projectDirPath = "";
127 | projectRoot = "";
128 | targets = (
129 | 627B5EA82321063E0046E23D /* KakaJSONDemo */,
130 | );
131 | };
132 | /* End PBXProject section */
133 |
134 | /* Begin PBXResourcesBuildPhase section */
135 | 627B5EA72321063E0046E23D /* Resources */ = {
136 | isa = PBXResourcesBuildPhase;
137 | buildActionMask = 2147483647;
138 | files = (
139 | 627B5EB7232106400046E23D /* LaunchScreen.storyboard in Resources */,
140 | 627B5EB4232106400046E23D /* Assets.xcassets in Resources */,
141 | 627B5EB22321063E0046E23D /* Main.storyboard in Resources */,
142 | );
143 | runOnlyForDeploymentPostprocessing = 0;
144 | };
145 | /* End PBXResourcesBuildPhase section */
146 |
147 | /* Begin PBXSourcesBuildPhase section */
148 | 627B5EA52321063E0046E23D /* Sources */ = {
149 | isa = PBXSourcesBuildPhase;
150 | buildActionMask = 2147483647;
151 | files = (
152 | 627B5EAF2321063E0046E23D /* ViewController.swift in Sources */,
153 | 627B5EAD2321063E0046E23D /* AppDelegate.swift in Sources */,
154 | );
155 | runOnlyForDeploymentPostprocessing = 0;
156 | };
157 | /* End PBXSourcesBuildPhase section */
158 |
159 | /* Begin PBXVariantGroup section */
160 | 627B5EB02321063E0046E23D /* Main.storyboard */ = {
161 | isa = PBXVariantGroup;
162 | children = (
163 | 627B5EB12321063E0046E23D /* Base */,
164 | );
165 | name = Main.storyboard;
166 | sourceTree = "";
167 | };
168 | 627B5EB5232106400046E23D /* LaunchScreen.storyboard */ = {
169 | isa = PBXVariantGroup;
170 | children = (
171 | 627B5EB6232106400046E23D /* Base */,
172 | );
173 | name = LaunchScreen.storyboard;
174 | sourceTree = "";
175 | };
176 | /* End PBXVariantGroup section */
177 |
178 | /* Begin XCBuildConfiguration section */
179 | 627B5EB9232106400046E23D /* Debug */ = {
180 | isa = XCBuildConfiguration;
181 | buildSettings = {
182 | ALWAYS_SEARCH_USER_PATHS = NO;
183 | CLANG_ANALYZER_NONNULL = YES;
184 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
185 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
186 | CLANG_CXX_LIBRARY = "libc++";
187 | CLANG_ENABLE_MODULES = YES;
188 | CLANG_ENABLE_OBJC_ARC = YES;
189 | CLANG_ENABLE_OBJC_WEAK = YES;
190 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
191 | CLANG_WARN_BOOL_CONVERSION = YES;
192 | CLANG_WARN_COMMA = YES;
193 | CLANG_WARN_CONSTANT_CONVERSION = YES;
194 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
195 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
196 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
197 | CLANG_WARN_EMPTY_BODY = YES;
198 | CLANG_WARN_ENUM_CONVERSION = YES;
199 | CLANG_WARN_INFINITE_RECURSION = YES;
200 | CLANG_WARN_INT_CONVERSION = YES;
201 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
202 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
203 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
204 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
205 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
206 | CLANG_WARN_STRICT_PROTOTYPES = YES;
207 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
208 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
209 | CLANG_WARN_UNREACHABLE_CODE = YES;
210 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
211 | CODE_SIGN_IDENTITY = "iPhone Developer";
212 | COPY_PHASE_STRIP = NO;
213 | DEBUG_INFORMATION_FORMAT = dwarf;
214 | ENABLE_STRICT_OBJC_MSGSEND = YES;
215 | ENABLE_TESTABILITY = YES;
216 | GCC_C_LANGUAGE_STANDARD = gnu11;
217 | GCC_DYNAMIC_NO_PIC = NO;
218 | GCC_NO_COMMON_BLOCKS = YES;
219 | GCC_OPTIMIZATION_LEVEL = 0;
220 | GCC_PREPROCESSOR_DEFINITIONS = (
221 | "DEBUG=1",
222 | "$(inherited)",
223 | );
224 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
225 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
226 | GCC_WARN_UNDECLARED_SELECTOR = YES;
227 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
228 | GCC_WARN_UNUSED_FUNCTION = YES;
229 | GCC_WARN_UNUSED_VARIABLE = YES;
230 | IPHONEOS_DEPLOYMENT_TARGET = 12.2;
231 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
232 | MTL_FAST_MATH = YES;
233 | ONLY_ACTIVE_ARCH = YES;
234 | SDKROOT = iphoneos;
235 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
236 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
237 | };
238 | name = Debug;
239 | };
240 | 627B5EBA232106400046E23D /* Release */ = {
241 | isa = XCBuildConfiguration;
242 | buildSettings = {
243 | ALWAYS_SEARCH_USER_PATHS = NO;
244 | CLANG_ANALYZER_NONNULL = YES;
245 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
246 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
247 | CLANG_CXX_LIBRARY = "libc++";
248 | CLANG_ENABLE_MODULES = YES;
249 | CLANG_ENABLE_OBJC_ARC = YES;
250 | CLANG_ENABLE_OBJC_WEAK = YES;
251 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
252 | CLANG_WARN_BOOL_CONVERSION = YES;
253 | CLANG_WARN_COMMA = YES;
254 | CLANG_WARN_CONSTANT_CONVERSION = YES;
255 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
257 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
258 | CLANG_WARN_EMPTY_BODY = YES;
259 | CLANG_WARN_ENUM_CONVERSION = YES;
260 | CLANG_WARN_INFINITE_RECURSION = YES;
261 | CLANG_WARN_INT_CONVERSION = YES;
262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
263 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
264 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
267 | CLANG_WARN_STRICT_PROTOTYPES = YES;
268 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
269 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
270 | CLANG_WARN_UNREACHABLE_CODE = YES;
271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
272 | CODE_SIGN_IDENTITY = "iPhone Developer";
273 | COPY_PHASE_STRIP = NO;
274 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
275 | ENABLE_NS_ASSERTIONS = NO;
276 | ENABLE_STRICT_OBJC_MSGSEND = YES;
277 | GCC_C_LANGUAGE_STANDARD = gnu11;
278 | GCC_NO_COMMON_BLOCKS = YES;
279 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
280 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
281 | GCC_WARN_UNDECLARED_SELECTOR = YES;
282 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
283 | GCC_WARN_UNUSED_FUNCTION = YES;
284 | GCC_WARN_UNUSED_VARIABLE = YES;
285 | IPHONEOS_DEPLOYMENT_TARGET = 12.2;
286 | MTL_ENABLE_DEBUG_INFO = NO;
287 | MTL_FAST_MATH = YES;
288 | SDKROOT = iphoneos;
289 | SWIFT_COMPILATION_MODE = wholemodule;
290 | SWIFT_OPTIMIZATION_LEVEL = "-O";
291 | VALIDATE_PRODUCT = YES;
292 | };
293 | name = Release;
294 | };
295 | 627B5EBC232106400046E23D /* Debug */ = {
296 | isa = XCBuildConfiguration;
297 | buildSettings = {
298 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
299 | CODE_SIGN_STYLE = Automatic;
300 | INFOPLIST_FILE = KakaJSONDemo/Info.plist;
301 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
302 | LD_RUNPATH_SEARCH_PATHS = (
303 | "$(inherited)",
304 | "@executable_path/Frameworks",
305 | );
306 | PRODUCT_BUNDLE_IDENTIFIER = com.github.kakaopensource.KakaJSONDemo;
307 | PRODUCT_NAME = "$(TARGET_NAME)";
308 | SWIFT_VERSION = 5.0;
309 | TARGETED_DEVICE_FAMILY = "1,2";
310 | };
311 | name = Debug;
312 | };
313 | 627B5EBD232106400046E23D /* Release */ = {
314 | isa = XCBuildConfiguration;
315 | buildSettings = {
316 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
317 | CODE_SIGN_STYLE = Automatic;
318 | INFOPLIST_FILE = KakaJSONDemo/Info.plist;
319 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
320 | LD_RUNPATH_SEARCH_PATHS = (
321 | "$(inherited)",
322 | "@executable_path/Frameworks",
323 | );
324 | PRODUCT_BUNDLE_IDENTIFIER = com.github.kakaopensource.KakaJSONDemo;
325 | PRODUCT_NAME = "$(TARGET_NAME)";
326 | SWIFT_VERSION = 5.0;
327 | TARGETED_DEVICE_FAMILY = "1,2";
328 | };
329 | name = Release;
330 | };
331 | /* End XCBuildConfiguration section */
332 |
333 | /* Begin XCConfigurationList section */
334 | 627B5EA42321063E0046E23D /* Build configuration list for PBXProject "KakaJSONDemo" */ = {
335 | isa = XCConfigurationList;
336 | buildConfigurations = (
337 | 627B5EB9232106400046E23D /* Debug */,
338 | 627B5EBA232106400046E23D /* Release */,
339 | );
340 | defaultConfigurationIsVisible = 0;
341 | defaultConfigurationName = Release;
342 | };
343 | 627B5EBB232106400046E23D /* Build configuration list for PBXNativeTarget "KakaJSONDemo" */ = {
344 | isa = XCConfigurationList;
345 | buildConfigurations = (
346 | 627B5EBC232106400046E23D /* Debug */,
347 | 627B5EBD232106400046E23D /* Release */,
348 | );
349 | defaultConfigurationIsVisible = 0;
350 | defaultConfigurationName = Release;
351 | };
352 | /* End XCConfigurationList section */
353 | };
354 | rootObject = 627B5EA12321063E0046E23D /* Project object */;
355 | }
356 |
--------------------------------------------------------------------------------
/KakaJSONDemo/KakaJSONDemo.xcodeproj/xcshareddata/xcschemes/KakaJSONDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/KakaJSONDemo/KakaJSONDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // KakaJSONDemo
4 | //
5 | // Created by Quentin MED on 2019/9/5.
6 | // Copyright © 2019 KakaJSON. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/KakaJSONDemo/KakaJSONDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/KakaJSONDemo/KakaJSONDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/KakaJSONDemo/KakaJSONDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/KakaJSONDemo/KakaJSONDemo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/KakaJSONDemo/KakaJSONDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/KakaJSONDemo/KakaJSONDemo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // KakaJSONDemo
4 | //
5 | // Created by Quentin MED on 2019/9/5.
6 | // Copyright © 2019 KakaJSON. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import KakaJSON
11 |
12 | class ViewController: UIViewController {
13 |
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 |
17 | struct Repo: Convertible {
18 | var name: String?
19 | var url: URL?
20 | }
21 |
22 | let json = [
23 | "name": "KakaJSON",
24 | "url": "https://github.com/kakaopensource/KakaJSON"
25 | ]
26 |
27 | let repo = json.kj.model(Repo.self)
28 | print(repo)
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 KakaOpenSource
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "KakaJSON",
8 | platforms: [
9 | .macOS(.v10_10),
10 | .iOS(.v8),
11 | .tvOS(.v9),
12 | .watchOS(.v2)
13 | ],
14 | products: [
15 | .library(
16 | name: "KakaJSON",
17 | targets: ["KakaJSON"]),
18 | ],
19 | targets: [
20 | .target(
21 | name: "KakaJSON",
22 | dependencies: []),
23 | .testTarget(
24 | name: "KakaJSONTests",
25 | dependencies: ["KakaJSON"]),
26 | ]
27 | )
28 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Convert/Convertible.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Convertible.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/11.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - Convertible Interface
12 |
13 | /// return value type of kj_modelKey
14 | public protocol ModelPropertyKey {}
15 | extension String: ModelPropertyKey {}
16 | extension Array: ModelPropertyKey where Element == String {}
17 |
18 | /// return value type of kj_JSONKey
19 | public typealias JSONPropertyKey = String
20 |
21 | /// all the models who want to convert should conform to `Convertible`
22 | /// please let struct or class conform to `Convertible`
23 | public protocol Convertible {
24 | init()
25 |
26 | /// Get a key from propertyName when converting JSON to model
27 | ///
28 | /// Only call once for every property in every type
29 | ///
30 | /// - Parameter property: A property of model
31 | ///
32 | /// - Returns: Return a key to get jsonValue for the property
33 | func kj_modelKey(from property: Property) -> ModelPropertyKey
34 |
35 | /// Get a model modelValue from jsonValue when converting JSON to model
36 | ///
37 | /// - Parameters:
38 | /// - jsonValue: A jsonValue for the property
39 | /// - property: A property of model
40 | ///
41 | /// - Returns: return `nil` indicates ignore the property,
42 | /// use the initial value instead.
43 | /// return `JSONValue` indicates do nothing
44 | func kj_modelValue(from jsonValue: Any?,
45 | _ property: Property) -> Any?
46 |
47 | /// model type for Any、AnyObject、Convertible...
48 | ///
49 | /// - Parameters:
50 | /// - jsonValue: A jsonValue for the property
51 | /// - property: A property of model
52 | func kj_modelType(from jsonValue: Any?,
53 | _ property: Property) -> Convertible.Type?
54 |
55 | /// call when will begin to convert from JSON to model
56 | ///
57 | /// - Parameters:
58 | /// - json: A json will be converted to model
59 | mutating func kj_willConvertToModel(from json: [String: Any])
60 |
61 | /// call when did finish converting from JSON to model
62 | ///
63 | /// - Parameters:
64 | /// - json: A json did be converted to model
65 | mutating func kj_didConvertToModel(from json: [String: Any])
66 |
67 | /// Get a key from propertyName when converting from model to JSON
68 | ///
69 | /// Only call once for every property in every type
70 | ///
71 | /// - Parameters:
72 | /// - property: A property of model
73 | func kj_JSONKey(from property: Property) -> JSONPropertyKey
74 |
75 | /// Get a JSONValue from modelValue when converting from JSON to model
76 | ///
77 | /// - Parameters:
78 | /// - modelValue: A modelValue for the property
79 | /// - property: A property of model
80 | ///
81 | /// - Returns: return `nil` indicates ignore the JSONValue.
82 | /// return `modelValue` indicates do nothing
83 | func kj_JSONValue(from modelValue: Any?,
84 | _ property: Property) -> Any?
85 |
86 | /// call when will begin to convert from model to JSON
87 | func kj_willConvertToJSON()
88 |
89 | /// call when did finish converting from model to JSON
90 | ///
91 | /// - Parameters:
92 | /// - json: A json did be converted from model
93 | func kj_didConvertToJSON(json: [String: Any])
94 | }
95 |
96 | /// default implementation
97 | public extension Convertible {
98 | func kj_modelKey(from property: Property) -> ModelPropertyKey {
99 | return ConvertibleConfig.modelKey(from: property, Self.self)
100 | }
101 | func kj_modelValue(from jsonValue: Any?,
102 | _ property: Property) -> Any? {
103 | return ConvertibleConfig.modelValue(from: jsonValue, property, Self.self)
104 | }
105 | func kj_modelType(from jsonValue: Any?,
106 | _ property: Property) -> Convertible.Type? { return nil }
107 | func kj_willConvertToModel(from json: [String: Any]) {}
108 | func kj_didConvertToModel(from json: [String: Any]) {}
109 |
110 | func kj_JSONKey(from property: Property) -> JSONPropertyKey {
111 | return ConvertibleConfig.JSONKey(from: property, Self.self)
112 | }
113 | func kj_JSONValue(from modelValue: Any?,
114 | _ property: Property) -> Any? {
115 | return ConvertibleConfig.JSONValue(from: modelValue, property, Self.self)
116 | }
117 | func kj_willConvertToJSON() {}
118 | func kj_didConvertToJSON(json: [String: Any]) {}
119 | }
120 |
121 | // MARK: - Wrapper for Convertible
122 | public extension Convertible {
123 | static var kj: ConvertibleKJ.Type {
124 | get { return ConvertibleKJ.self }
125 | set {}
126 | }
127 | var kj: ConvertibleKJ {
128 | get { return ConvertibleKJ(self) }
129 | set {}
130 | }
131 |
132 | /// mutable version of kj
133 | var kj_m: ConvertibleKJ_M {
134 | mutating get { return ConvertibleKJ_M(&self) }
135 | set {}
136 | }
137 | }
138 |
139 | public struct ConvertibleKJ_M {
140 | var basePtr: UnsafeMutablePointer
141 | init(_ basePtr: UnsafeMutablePointer) {
142 | self.basePtr = basePtr
143 | }
144 |
145 | /// JSONData -> Model
146 | public func convert(from jsonData: Data) {
147 | basePtr.pointee.kj_convert(from: jsonData)
148 | }
149 |
150 | /// JSONData -> Model
151 | public func convert(from jsonData: NSData) {
152 | basePtr.pointee.kj_convert(from: jsonData as Data)
153 | }
154 |
155 | /// JSONString -> Model
156 | public func convert(from jsonString: String) {
157 | basePtr.pointee.kj_convert(from: jsonString)
158 | }
159 |
160 | /// JSONString -> Model
161 | public func convert(from jsonString: NSString) {
162 | basePtr.pointee.kj_convert(from: jsonString as String)
163 | }
164 |
165 | /// JSONObject -> Model
166 | public func convert(from json: [String: Any]) {
167 | basePtr.pointee.kj_convert(from: json)
168 | }
169 |
170 | /// JSONObject -> Model
171 | public func convert(from json: [NSString: Any]) {
172 | basePtr.pointee.kj_convert(from: json as [String: Any])
173 | }
174 |
175 | /// JSONObject -> Model
176 | public func convert(from json: NSDictionary) {
177 | basePtr.pointee.kj_convert(from: json as! [String: Any])
178 | }
179 | }
180 |
181 | public struct ConvertibleKJ {
182 | var base: T
183 | init(_ base: T) {
184 | self.base = base
185 | }
186 |
187 | /// Model -> JSONObject
188 | public func JSONObject() -> [String: Any] {
189 | return base.kj_JSONObject()
190 | }
191 |
192 | /// Model -> JSONString
193 | public func JSONString(prettyPrinted: Bool = false) -> String {
194 | return base.kj_JSONString(prettyPrinted: prettyPrinted)
195 | }
196 | }
197 |
198 | private extension Convertible {
199 | /// get the ponter of model
200 | mutating func _ptr() -> UnsafeMutableRawPointer {
201 | return (Metadata.type(self)!.kind == .struct)
202 | ? withUnsafeMutablePointer(to: &self) { UnsafeMutableRawPointer($0) }
203 | : self ~>> UnsafeMutableRawPointer.self
204 | }
205 | }
206 |
207 | // MARK: - JSON -> Model
208 | extension Convertible {
209 | mutating func kj_convert(from jsonData: Data) {
210 | if let json = JSONSerialization.kj_JSON(jsonData, [String: Any].self) {
211 | kj_convert(from: json)
212 | return
213 | }
214 | Logger.error("Failed to get JSON from JSONData.")
215 | }
216 |
217 | mutating func kj_convert(from jsonString: String) {
218 | if let json = JSONSerialization.kj_JSON(jsonString, [String: Any].self) {
219 | kj_convert(from: json)
220 | return
221 | }
222 | Logger.error("Failed to get JSON from JSONString.")
223 | }
224 |
225 | mutating func kj_convert(from json: [String: Any]) {
226 | guard let mt = Metadata.type(self) as? ModelType else {
227 | Logger.warnning("Not a class or struct instance.")
228 | return
229 | }
230 | guard let properties = mt.properties else {
231 | Logger.warnning("Don't have any property.")
232 | return
233 | }
234 |
235 | // get data address
236 | let model = _ptr()
237 |
238 | kj_willConvertToModel(from: json)
239 |
240 | // enumerate properties
241 | for property in properties {
242 | // key filter
243 | let key = mt.modelKey(from: property.name,
244 | kj_modelKey(from: property))
245 |
246 | // value filter
247 | guard let newValue = kj_modelValue(
248 | from: json.kj_value(for: key),
249 | property)~! else { continue }
250 |
251 | let propertyType = property.dataType
252 | // if they are the same type, set value directly
253 | if Swift.type(of: newValue) == propertyType {
254 | property.set(newValue, for: model)
255 | continue
256 | }
257 |
258 | // Model Type have priority
259 | // it can return subclass object to match superclass type
260 | if let modelType = kj_modelType(from: newValue, property),
261 | let value = _modelTypeValue(newValue, modelType, propertyType) {
262 | property.set(value, for: model)
263 | continue
264 | }
265 |
266 | // try to convert newValue to propertyType
267 | guard let value = Values.value(newValue,
268 | propertyType,
269 | property.get(from: model)) else {
270 | property.set(newValue, for: model)
271 | continue
272 | }
273 |
274 | property.set(value, for: model)
275 | }
276 |
277 | kj_didConvertToModel(from: json)
278 | }
279 |
280 | private mutating
281 | func _modelTypeValue(_ jsonValue: Any,
282 | _ modelType: Convertible.Type,
283 | _ propertyType: Any.Type) -> Any? {
284 | // don't use `propertyType is XX.Type`
285 | // because it may be an `Any` type
286 | if let json = jsonValue as? [Any] {
287 | let models = json.kj.modelArray(type: modelType)
288 | if !models.isEmpty {
289 | return propertyType is NSMutableArray.Type
290 | ? NSMutableArray(array: models)
291 | : models
292 | }
293 | }
294 |
295 | if let json = jsonValue as? [String: Any] {
296 | if let jsonDict = jsonValue as? [String: [String: Any]?] {
297 | var modelDict = [String: Any]()
298 | for (k, v) in jsonDict {
299 | guard let m = v?.kj.model(type: modelType) else { continue }
300 | modelDict[k] = m
301 | }
302 | guard modelDict.count > 0 else { return jsonValue }
303 |
304 | return propertyType is NSMutableDictionary.Type
305 | ? NSMutableDictionary(dictionary: modelDict)
306 | : modelDict
307 | } else {
308 | return json.kj.model(type: modelType)
309 | }
310 | }
311 | return jsonValue
312 | }
313 | }
314 |
315 | // MARK: - Model -> JSON
316 | extension Convertible {
317 | func kj_JSONObject() -> [String: Any] {
318 | var json = [String: Any]()
319 | guard let mt = Metadata.type(self) as? ModelType else {
320 | Logger.warnning("Not a class or struct instance.")
321 | return json
322 | }
323 | guard let properties = mt.properties else {
324 | Logger.warnning("Don't have any property.")
325 | return json
326 | }
327 |
328 | kj_willConvertToJSON()
329 |
330 | // as AnyObject is important! for class if xx is protocol type
331 | // var model = xx as AnyObject
332 | var model = self
333 |
334 | // get data address
335 | let ptr = model._ptr()
336 |
337 | // get JSON from model
338 | for property in properties {
339 | // value filter
340 | guard let value = kj_JSONValue(
341 | from: property.get(from: ptr)~!,
342 | property)~! else { continue }
343 |
344 | guard let v = Values.JSONValue(value) else { continue }
345 |
346 | // key filter
347 | json[mt.JSONKey(from: property.name,
348 | kj_JSONKey(from: property))] = v
349 | }
350 |
351 | kj_didConvertToJSON(json: json)
352 |
353 | return json
354 | }
355 |
356 | func kj_JSONString(prettyPrinted: Bool = false) -> String {
357 | if let str = JSONSerialization.kj_string(kj_JSONObject() as Any,
358 | prettyPrinted: prettyPrinted) {
359 | return str
360 | }
361 | Logger.error("Failed to get JSONString from JSON.")
362 | return ""
363 | }
364 | }
365 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Convert/ConvertibleConfig.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConvertibleConfig.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/15.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public typealias ModelKeyConfig = (Property) -> ModelPropertyKey
12 | public typealias ModelValueConfig = (Any?, Property) -> Any?
13 | public typealias JSONKeyConfig = (Property) -> JSONPropertyKey
14 | public typealias JSONValueConfig = (Any?, Property) -> Any?
15 |
16 | /// Config for convertible
17 | ///
18 | /// Suggest:
19 | /// 1. Set every config once for every type
20 | /// 2. Set config after app finish lauching
21 | public class ConvertibleConfig {
22 | private class Item {
23 | var modelKey: ModelKeyConfig?
24 | var modelValue: ModelValueConfig?
25 | var jsonKey: JSONKeyConfig?
26 | var jsonValue: JSONValueConfig?
27 | init() {}
28 | init(modelKey: @escaping ModelKeyConfig) { self.modelKey = modelKey }
29 | init(modelValue: @escaping ModelValueConfig) { self.modelValue = modelValue }
30 | init(jsonKey: @escaping JSONKeyConfig) { self.jsonKey = jsonKey }
31 | init(jsonValue: @escaping JSONValueConfig) { self.jsonValue = jsonValue }
32 | }
33 |
34 | private static let global = Item()
35 | private static var items: [TypeKey: Item] = [:]
36 |
37 | /// get global config for modelKey
38 | public static func modelKey(from property: Property) -> ModelPropertyKey {
39 | guard let fn = global.modelKey else { return property.name }
40 | return fn(property)
41 | }
42 |
43 | /// get model's config for modelKey
44 | public static func modelKey(from property: Property,
45 | _ model: Convertible) -> ModelPropertyKey {
46 | return modelKey(from: property, type(of: model))
47 | }
48 |
49 | /// get type's config for modelKey
50 | public static func modelKey(from property: Property,
51 | _ type: Convertible.Type) -> ModelPropertyKey {
52 | if let fn = items[typeKey(type)]?.modelKey {
53 | return fn(property)
54 | }
55 |
56 | let mt = Metadata.type(type)
57 | if var classMt = mt as? ClassType {
58 | while let superMt = classMt.super {
59 | if let fn = items[typeKey(superMt.type)]?.modelKey {
60 | return fn(property)
61 | }
62 | classMt = superMt
63 | }
64 | }
65 |
66 | guard let fn = global.modelKey else {
67 | return property.name
68 | }
69 | return fn(property)
70 | }
71 |
72 | /// get global config for modelValue
73 | public static func modelValue(from jsonValue: Any?,
74 | _ property: Property) -> Any? {
75 | guard let fn = global.modelValue else { return jsonValue }
76 | return fn(jsonValue, property)
77 | }
78 |
79 | /// get model's config for modelValue
80 | public static func modelValue(from jsonValue: Any?,
81 | _ property: Property,
82 | _ model: Convertible) -> Any? {
83 | return modelValue(from: jsonValue, property, type(of: model))
84 | }
85 |
86 | /// get type's config for modelValue
87 | public static func modelValue(from jsonValue: Any?,
88 | _ property: Property,
89 | _ type: Convertible.Type) -> Any? {
90 | let key = typeKey(type)
91 | if let fn = items[key]?.modelValue {
92 | return fn(jsonValue, property)
93 | }
94 |
95 | let mt = Metadata.type(type)
96 | if var classMt = mt as? ClassType {
97 | while let superMt = classMt.super {
98 | if let fn = items[typeKey(superMt.type)]?.modelValue {
99 | items[key]?.modelValue = fn
100 | return fn(jsonValue, property)
101 | }
102 | classMt = superMt
103 | }
104 | }
105 |
106 | guard let fn = global.modelValue else {
107 | items[key]?.modelValue = { v, _ in v }
108 | return jsonValue
109 | }
110 | items[key]?.modelValue = fn
111 | return fn(jsonValue, property)
112 | }
113 |
114 | /// get global config for JSONKey
115 | public static func JSONKey(from property: Property) -> JSONPropertyKey {
116 | guard let fn = global.jsonKey else { return property.name }
117 | return fn(property)
118 | }
119 |
120 | /// get model's config for JSONKey
121 | public static func JSONKey(from property: Property,
122 | _ model: Convertible) -> JSONPropertyKey {
123 | return JSONKey(from: property, type(of: model))
124 | }
125 |
126 | /// get type's config for JSONKey
127 | public static func JSONKey(from property: Property,
128 | _ type: Convertible.Type) -> JSONPropertyKey {
129 | if let fn = items[typeKey(type)]?.jsonKey {
130 | return fn(property)
131 | }
132 |
133 | let mt = Metadata.type(type)
134 | if var classMt = mt as? ClassType {
135 | while let superMt = classMt.super {
136 | if let fn = items[typeKey(superMt.type)]?.jsonKey {
137 | return fn(property)
138 | }
139 | classMt = superMt
140 | }
141 | }
142 |
143 | guard let fn = global.jsonKey else {
144 | return property.name
145 | }
146 | return fn(property)
147 | }
148 |
149 | /// get global config for JSONValue
150 | public static func JSONValue(from modelValue: Any?,
151 | _ property: Property) -> Any? {
152 | guard let fn = global.modelValue else { return modelValue }
153 | return fn(modelValue, property)
154 | }
155 |
156 | /// get model's config for JSONValue
157 | public static func JSONValue(from modelValue: Any?,
158 | _ property: Property,
159 | _ model: Convertible) -> Any? {
160 | return JSONValue(from: modelValue, property, type(of: model))
161 | }
162 |
163 | /// get type's config for JSONValue
164 | public static func JSONValue(from modelValue: Any?,
165 | _ property: Property,
166 | _ type: Convertible.Type) -> Any? {
167 | let key = typeKey(type)
168 | if let fn = items[key]?.jsonValue {
169 | return fn(modelValue, property)
170 | }
171 |
172 | let mt = Metadata.type(type)
173 | if var classMt = mt as? ClassType {
174 | while let superMt = classMt.super {
175 | if let fn = items[typeKey(superMt.type)]?.jsonValue {
176 | items[key]?.jsonValue = fn
177 | return fn(modelValue, property)
178 | }
179 | classMt = superMt
180 | }
181 | }
182 |
183 | guard let fn = global.modelValue else {
184 | items[key]?.jsonValue = { v, _ in v}
185 | return modelValue
186 | }
187 | items[key]?.jsonValue = fn
188 | return fn(modelValue, property)
189 | }
190 |
191 | /// set type's config for modelKey
192 | public static func setModelKey(for type: Convertible.Type,
193 | _ modelKey: @escaping ModelKeyConfig) {
194 | setModelKey(for: [type], modelKey)
195 | }
196 |
197 | /// set types's config for modelKey
198 | public static func setModelKey(for types: [Convertible.Type] = [],
199 | _ modelKey: @escaping ModelKeyConfig) {
200 | if types.count == 0 {
201 | global.modelKey = modelKey
202 | return
203 | }
204 |
205 | types.forEach {
206 | let key = typeKey($0)
207 | if let item = items[key] {
208 | item.modelKey = modelKey
209 | return
210 | }
211 | items[key] = Item(modelKey: modelKey)
212 | }
213 | }
214 |
215 | /// set type's config for modelValue
216 | public static func setModelValue(for type: Convertible.Type,
217 | modelValue: @escaping ModelValueConfig) {
218 | setModelValue(for: [type], modelValue: modelValue)
219 | }
220 |
221 | /// set types's config for modelValue
222 | public static func setModelValue(for types: [Convertible.Type] = [],
223 | modelValue: @escaping ModelValueConfig) {
224 | if types.count == 0 {
225 | global.modelValue = modelValue
226 | return
227 | }
228 |
229 | types.forEach {
230 | let key = typeKey($0)
231 | if let item = items[key] {
232 | item.modelValue = modelValue
233 | return
234 | }
235 | items[key] = Item(modelValue: modelValue)
236 | }
237 | }
238 |
239 | /// set type's config for jsonKey
240 | public static func setJSONKey(for type: Convertible.Type,
241 | jsonKey: @escaping JSONKeyConfig) {
242 | setJSONKey(for: [type], jsonKey: jsonKey)
243 | }
244 |
245 | /// set types's config for jsonKey
246 | public static func setJSONKey(for types: [Convertible.Type] = [],
247 | jsonKey: @escaping JSONKeyConfig) {
248 | if types.count == 0 {
249 | global.jsonKey = jsonKey
250 | return
251 | }
252 |
253 | types.forEach {
254 | let key = typeKey($0)
255 | if let item = items[key] {
256 | item.jsonKey = jsonKey
257 | return
258 | }
259 | items[key] = Item(jsonKey: jsonKey)
260 | }
261 | }
262 |
263 | /// set type's config for jsonValue
264 | public static func setJSONValue(for type: Convertible.Type,
265 | jsonValue: @escaping JSONValueConfig) {
266 | setJSONValue(for: [type], jsonValue: jsonValue)
267 | }
268 |
269 | /// set types's config for jsonValue
270 | public static func setJSONValue(for types: [Convertible.Type] = [],
271 | jsonValue: @escaping JSONValueConfig) {
272 | if types.count == 0 {
273 | global.jsonValue = jsonValue
274 | return
275 | }
276 |
277 | types.forEach {
278 | let key = typeKey($0)
279 | if let item = items[key] {
280 | item.jsonValue = jsonValue
281 | return
282 | }
283 | items[key] = Item(jsonValue: jsonValue)
284 | }
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Convert/ConvertibleEnum.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConvertibleEnum.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/13.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | public protocol ConvertibleEnum {
10 | static func kj_convert(from value: Any) -> Self?
11 | var kj_value: Any { get }
12 | static var kj_valueType: Any.Type { get }
13 | }
14 |
15 | public extension RawRepresentable where Self: ConvertibleEnum {
16 | static func kj_convert(from value: Any) -> Self? {
17 | return (value as? RawValue).flatMap { Self.init(rawValue: $0) }
18 | }
19 | var kj_value: Any { return rawValue }
20 | static var kj_valueType: Any.Type { return RawValue.self }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Convert/KJ.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KJ.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/30.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | public struct KJ {
10 | let base: Base
11 | init(_ base: Base) {
12 | self.base = base
13 | }
14 | }
15 |
16 | // MARK: - protocol for normal types
17 | public protocol KJCompatible {}
18 | public extension KJCompatible {
19 | static var kj: KJ.Type {
20 | get { return KJ.self }
21 | set {}
22 | }
23 | var kj: KJ {
24 | get { return KJ(self) }
25 | set {}
26 | }
27 | }
28 |
29 | // MARK: - protocol for types with a generic parameter
30 | public struct KJGeneric {
31 | let base: Base
32 | init(_ base: Base) {
33 | self.base = base
34 | }
35 | }
36 | public protocol KJGenericCompatible {
37 | associatedtype T
38 | }
39 | public extension KJGenericCompatible {
40 | static var kj: KJGeneric.Type {
41 | get { return KJGeneric.self }
42 | set {}
43 | }
44 | var kj: KJGeneric {
45 | get { return KJGeneric(self) }
46 | set {}
47 | }
48 | }
49 |
50 | // MARK: - protocol for types with two generic parameter2
51 | public struct KJGeneric2 {
52 | let base: Base
53 | init(_ base: Base) {
54 | self.base = base
55 | }
56 | }
57 | public protocol KJGenericCompatible2 {
58 | associatedtype T1
59 | associatedtype T2
60 | }
61 | public extension KJGenericCompatible2 {
62 | static var kj: KJGeneric2.Type {
63 | get { return KJGeneric2.self }
64 | set {}
65 | }
66 | var kj: KJGeneric2 {
67 | get { return KJGeneric2(self) }
68 | set {}
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Convert/Logger.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Logger.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/2.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Logger {
12 | private static func _log(_ title: String, _ msg: T, _ file: NSString,
13 | _ fn: String, _ line: Int) {
14 | #if DEBUG
15 | print("「 KakaJSON 」\(title) \(file.lastPathComponent) >> \(line) >> \(fn) :\(msg)")
16 | #endif
17 | }
18 |
19 | static func error(_ msg: T,
20 | _ file: NSString = #file,
21 | _ fn: String = #function,
22 | _ line: Int = #line) {
23 | _log("❌", msg, file, fn, line)
24 | }
25 |
26 | static func warnning(_ msg: T,
27 | _ file: NSString = #file,
28 | _ fn: String = #function,
29 | _ line: Int = #line) {
30 | _log("⚠️", msg, file, fn, line)
31 | }
32 |
33 | static func success(_ msg: T,
34 | _ file: NSString = #file,
35 | _ fn: String = #function,
36 | _ line: Int = #line) {
37 | _log("✔️", msg, file, fn, line)
38 | }
39 |
40 | static func info(_ msg: T,
41 | _ file: NSString = #file,
42 | _ fn: String = #function,
43 | _ line: Int = #line) {
44 | _log("✔️", msg, file, fn, line)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Convert/TypeProxy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TypeProxy.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/13.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | protocol TypeProxy: Convertible {}
10 |
11 | extension TypeProxy {
12 | static func `is`(_ value: Any) -> Bool {
13 | return value is Self
14 | }
15 |
16 | static func `is`(_ type: Any.Type) -> Bool {
17 | return type is Self.Type
18 | }
19 |
20 | static func `as`(_ value: Any) -> Self? {
21 | return value as? Self
22 | }
23 |
24 | static func `as`(_ type: Any.Type) -> Self.Type? {
25 | return type as? Self.Type
26 | }
27 | }
28 |
29 | func typeProxy(_ type: Any.Type) -> TypeProxy.Type {
30 | // Any.Type(8 bytes) + Int(8 bytes) == Protocol.Type(16 bytes)
31 | return (type, 0) ~>> TypeProxy.Type.self
32 | }
33 |
34 | typealias TypeKey = UInt
35 | func typeKey(_ type: Any.Type) -> TypeKey {
36 | return type ~>> TypeKey.self
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Extension/Array+KJ.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+KJ.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/12.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension NSArray: KJCompatible {}
12 | extension Array: KJCompatible {}
13 | extension Set: KJCompatible {}
14 | extension NSSet: KJCompatible {}
15 |
16 | public extension KJ where Base: ExpressibleByArrayLiteral & Sequence {
17 | /// JSONObjectArray -> EnumArray
18 | func enumArray(_ type: M.Type) -> [M] {
19 | return enumArray(type: type) as! [M]
20 | }
21 |
22 | /// JSONObjectArray -> EnumArray
23 | func enumArray(type: ConvertibleEnum.Type) -> [ConvertibleEnum] {
24 | guard let _ = Metadata.type(type) as? EnumType else { return [] }
25 | return base.compactMap {
26 | let vv = Values.value($0, type.kj_valueType)
27 | return type.kj_convert(from: vv as Any)
28 | }
29 | }
30 |
31 | /// JSONObjectArray -> ModelArray
32 | func modelArray(_ type: M.Type) -> [M] {
33 | return modelArray(type: type) as! [M]
34 | }
35 |
36 | /// JSONObjectArray -> ModelArray
37 | func modelArray(type: Convertible.Type) -> [Convertible] {
38 | guard let mt = Metadata.type(type) as? ModelType,
39 | let _ = mt.properties
40 | else { return [] }
41 | return base.compactMap {
42 | switch $0 {
43 | case let dict as [String: Any]: return dict.kj_fastModel(type)
44 | case let dict as Data: return dict.kj_fastModel(type)
45 | case let dict as String: return dict.kj_fastModel(type)
46 | default: return nil
47 | }
48 | }
49 | }
50 |
51 | /// ModelArray -> JSONObjectArray
52 | func JSONObjectArray() -> [[String: Any]] {
53 | return base.compactMap {
54 | ($0~! as? Convertible)?.kj_JSONObject()
55 | }
56 | }
57 |
58 | /// Array -> JSONArray
59 | func JSONArray() -> [Any] {
60 | return Values.JSONValue(base) as! [Any]
61 | }
62 |
63 | /// Array -> JSONString
64 | func JSONString(prettyPrinted: Bool = false) -> String {
65 | if let str = JSONSerialization.kj_string(JSONArray(),
66 | prettyPrinted: prettyPrinted) {
67 | return str
68 | }
69 | Logger.error("Failed to get JSONString from JSON.")
70 | return ""
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Extension/Data+KJ.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Data+KJ.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/18.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Data: KJCompatible {}
12 | extension NSData: KJCompatible {}
13 |
14 | public extension KJ where Base == Data {
15 | /// JSONObject -> Model
16 | func model(_ type: M.Type) -> M? {
17 | return model(type: type) as? M
18 | }
19 |
20 | /// JSONObject -> Model
21 | func model(type: Convertible.Type) -> Convertible? {
22 | if let json = JSONSerialization.kj_JSON(base, [String: Any].self) {
23 | return json.kj.model(type: type)
24 | }
25 | Logger.error("Failed to get JSON from JSONData.")
26 | return nil
27 | }
28 |
29 | /// JSONObjectArray -> ModelArray
30 | func modelArray(_ type: M.Type) -> [M] {
31 | return modelArray(type: type) as! [M]
32 | }
33 |
34 | /// JSONObjectArray -> ModelArray
35 | func modelArray(type: Convertible.Type) -> [Convertible] {
36 | if let json = JSONSerialization.kj_JSON(base, [Any].self) {
37 | return json.kj.modelArray(type: type)
38 | }
39 | Logger.error("Failed to get JSON from JSONData.")
40 | return []
41 | }
42 | }
43 |
44 | public extension KJ where Base: NSData {
45 | /// JSONObject -> Model
46 | func model(_ type: M.Type) -> M? {
47 | return (base as Data).kj.model(type)
48 | }
49 |
50 | /// JSONObject -> Model
51 | func model(type: Convertible.Type) -> Convertible? {
52 | return (base as Data).kj.model(type: type)
53 | }
54 |
55 | /// JSONObjectArray -> ModelArray
56 | func modelArray(_ type: M.Type) -> [M] {
57 | return (base as Data).kj.modelArray(type)
58 | }
59 |
60 | /// JSONObjectArray -> ModelArray
61 | func modelArray(type: Convertible.Type) -> [Convertible] {
62 | return (base as Data).kj.modelArray(type: type)
63 | }
64 | }
65 |
66 | extension Data {
67 | func kj_fastModel(_ type: Convertible.Type) -> Convertible? {
68 | if let json = JSONSerialization.kj_JSON(self, [String: Any].self) {
69 | return json.kj_fastModel(type)
70 | }
71 | Logger.error("Failed to get JSON from JSONData.")
72 | return nil
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Extension/Dictionary+KJ.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Dictionary+KJ.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/7.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension NSDictionary: KJCompatible {}
12 | extension Dictionary: KJGenericCompatible {
13 | public typealias T = Value
14 | }
15 |
16 | public extension KJGeneric where Base == [String: T] {
17 | /// JSONObject -> Model
18 | func model(_ type: M.Type) -> M {
19 | return model(type: type) as! M
20 | }
21 |
22 | /// JSONObject -> Model
23 | func model(type: Convertible.Type) -> Convertible {
24 | return base.kj_fastModel(type)
25 | }
26 | }
27 |
28 | public extension KJGeneric where Base == [NSString: T] {
29 | /// JSONObject -> Model
30 | func model(_ type: M.Type) -> M {
31 | return (base as [String: Any]).kj.model(type)
32 | }
33 |
34 | /// JSONObject -> Model
35 | func model(type: Convertible.Type) -> Convertible {
36 | return (base as [String: Any]).kj.model(type: type)
37 | }
38 | }
39 |
40 | public extension KJ where Base: NSDictionary {
41 | /// JSONObject -> Model
42 | func model(_ type: M.Type) -> M? {
43 | return (base as? [String: Any])?.kj.model(type)
44 | }
45 |
46 | /// JSONObject -> Model
47 | func model(type: Convertible.Type) -> Convertible? {
48 | return (base as? [String: Any])?.kj.model(type: type)
49 | }
50 | }
51 |
52 | extension Dictionary where Key == String {
53 | func kj_fastModel(_ type: Convertible.Type) -> Convertible {
54 | var model: Convertible
55 | if let ns = type as? NSObject.Type {
56 | model = ns.newConvertible()
57 | } else {
58 | model = type.init()
59 | }
60 | model.kj_convert(from: self)
61 | return model
62 | }
63 |
64 | func kj_value(for modelPropertyKey: ModelPropertyKey) -> Any? {
65 | if let key = modelPropertyKey as? String {
66 | return _value(stringKey: key)
67 | }
68 |
69 | let keyArray = modelPropertyKey as! [String]
70 | for key in keyArray {
71 | if let value = _value(stringKey: key) {
72 | return value
73 | }
74 | }
75 | return nil
76 | }
77 |
78 | private func _value(stringKey: String) -> Any? {
79 | if let value = self[stringKey] {
80 | return value
81 | }
82 |
83 | let subkeys = stringKey.split(separator: ".")
84 | var value: Any? = self
85 | for subKey in subkeys {
86 | if let dict = value as? [String: Any] {
87 | value = dict[String(subKey)]
88 | // when nil, end the for-loop
89 | if value == nil { return nil }
90 | } else if let array = value as? [Any] {
91 | guard let index = Int(subKey),
92 | case array.indices = index else { return nil }
93 | value = array[index]
94 | } else {
95 | break
96 | }
97 | }
98 | return value
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Extension/JSONSerialization+KJ.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JSONSerialization+KJ.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/2.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension JSONSerialization {
12 | static func kj_JSON(_ string: String, _ type: T.Type) -> T? {
13 | return string.data(using: .utf8).flatMap { kj_JSON($0, type) }
14 | }
15 |
16 | static func kj_JSON(_ data: Data, _ type: T.Type) -> T? {
17 | return try? jsonObject(with: data,
18 | options: .allowFragments) as? T
19 | }
20 |
21 | static func kj_string(_ json: Any?,
22 | prettyPrinted: Bool = false) -> String? {
23 | guard let value = json.kj_value else { return nil }
24 | guard value is [Any] || value is [String: Any] else {
25 | return "\(value)"
26 | }
27 | guard let data = try? data(withJSONObject: value,
28 | options: prettyPrinted ? [.prettyPrinted] : [])
29 | else { return nil }
30 | return String(data: data, encoding: .utf8)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Extension/NSObject+KJ.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+KJ.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/15.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension NSObject {
12 | static func newConvertible() -> Convertible {
13 | return self.init() as! Convertible
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Extension/Optional+KJ.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Optional+KJ.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/5.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol OptionalValue {
12 | var kj_value: Any? { get }
13 | }
14 |
15 | extension Optional: OptionalValue {
16 | var kj_value: Any? {
17 | guard let v = self else { return nil }
18 | return (v as? OptionalValue)?.kj_value ?? v
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Extension/Pointer+KJ.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Pointer+KJ.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/1.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | extension UnsafePointer {
10 | var kj_raw: UnsafeRawPointer {
11 | return UnsafeRawPointer(self)
12 | }
13 | var kj_mutable: UnsafeMutablePointer {
14 | return UnsafeMutablePointer(mutating: self)
15 | }
16 | }
17 |
18 | extension UnsafeMutablePointer {
19 | var kj_raw: UnsafeMutableRawPointer {
20 | return UnsafeMutableRawPointer(self)
21 | }
22 |
23 | var kj_immutable: UnsafePointer {
24 | return UnsafePointer(self)
25 | }
26 | }
27 |
28 | extension UnsafeRawPointer {
29 | var kj_mutable: UnsafeMutableRawPointer {
30 | return UnsafeMutableRawPointer(mutating: self)
31 | }
32 |
33 | static func ~>(ptr: UnsafeRawPointer, type: T.Type) -> UnsafePointer {
34 | return ptr.assumingMemoryBound(to: type)
35 | }
36 | }
37 |
38 | extension UnsafeMutableRawPointer {
39 | var kj_immutable: UnsafeRawPointer {
40 | return UnsafeRawPointer(self)
41 | }
42 |
43 | func kj_set(_ value: Any, _ type: Any.Type) {
44 | return typeProxy(type)._set(value, self)
45 | }
46 |
47 | func kj_get(_ type: Any.Type) -> Any {
48 | return typeProxy(type)._get(self)
49 | }
50 |
51 | static func ~>(ptr: UnsafeMutableRawPointer, type: T.Type) -> UnsafeMutablePointer {
52 | return ptr.assumingMemoryBound(to: type)
53 | }
54 | }
55 |
56 | private extension TypeProxy {
57 | static func _set(_ value: Any, _ ptr: UnsafeMutableRawPointer) {
58 | (value as? Self).flatMap { (ptr ~> self).pointee = $0 }
59 | }
60 |
61 | static func _get(_ ptr: UnsafeMutableRawPointer) -> Any {
62 | return (ptr ~> self).pointee
63 | }
64 | }
65 |
66 | infix operator ~>> : MultiplicationPrecedence
67 | func ~>> (type1: T1, type2: T2.Type) -> T2 {
68 | return unsafeBitCast(type1, to: type2)
69 | }
70 |
71 | infix operator ~> : MultiplicationPrecedence
72 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Extension/String+KJ.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+KJ.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/5.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension String: KJCompatible {}
12 | extension NSString: KJCompatible {}
13 |
14 | public extension KJ where Base: ExpressibleByStringLiteral {
15 | /// from underline-cased to camel-cased
16 | ///
17 | /// e.g. from `my_test_name` to `myTestName`
18 | func camelCased() -> String {
19 | guard let str = base as? String else { return "" }
20 | var newStr = ""
21 | var upper = false
22 | for c in str {
23 | if c == "_" {
24 | upper = true
25 | continue
26 | }
27 |
28 | if upper, newStr.count > 0 {
29 | newStr += c.uppercased()
30 | } else {
31 | newStr.append(c)
32 | }
33 | upper = false
34 | }
35 | return newStr
36 | }
37 |
38 | /// from camel-cased to underline-cased
39 | ///
40 | /// e.g. from `myTestName` to `my_test_name`
41 | func underlineCased() -> String {
42 | guard let str = base as? String else { return "" }
43 | var newStr = ""
44 | for c in str {
45 | if c.isUppercase {
46 | newStr += "_"
47 | newStr += c.lowercased()
48 | } else {
49 | newStr.append(c)
50 | }
51 | }
52 | return newStr
53 | }
54 |
55 | /// JSONObject -> Model
56 | func model(_ type: M.Type) -> M? {
57 | return model(type: type) as? M
58 | }
59 |
60 | /// JSONObject -> Model
61 | func model(type: Convertible.Type) -> Convertible? {
62 | guard let string = base as? String else { return nil }
63 | return string.kj_fastModel(type)
64 | }
65 |
66 | /// JSONObjectArray -> ModelArray
67 | func modelArray(_ type: M.Type) -> [M] {
68 | return modelArray(type: type) as! [M]
69 | }
70 |
71 | /// JSONObjectArray -> ModelArray
72 | func modelArray(type: Convertible.Type) -> [Convertible] {
73 | guard let string = base as? String else { return [] }
74 | if let json = JSONSerialization.kj_JSON(string, [Any].self) {
75 | return json.kj.modelArray(type: type)
76 | }
77 | Logger.error("Failed to get JSON from JSONString.")
78 | return []
79 | }
80 | }
81 |
82 | extension String {
83 | func kj_fastModel(_ type: Convertible.Type) -> Convertible? {
84 | if let json = JSONSerialization.kj_JSON(self, [String: Any].self) {
85 | return json.kj_fastModel(type)
86 | }
87 | Logger.error("Failed to get JSON from JSONString.")
88 | return nil
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Global/Coding.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Coding.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/22.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | @discardableResult
12 | public func write(_ value: Any,
13 | to file: String,
14 | atomically: Bool = true,
15 | encoding: String.Encoding = .utf8) -> String? {
16 | return write(value, to: URL(fileURLWithPath: file), encoding: encoding)
17 | }
18 |
19 | @discardableResult
20 | public func write(_ value: Any,
21 | to URL: URL,
22 | atomically: Bool = true,
23 | encoding: String.Encoding = .utf8) -> String? {
24 | if URL.isFileURL {
25 | let dir = (URL.path as NSString).deletingLastPathComponent
26 | let mgr = FileManager.default
27 | if !mgr.fileExists(atPath: dir) {
28 | try? mgr.createDirectory(atPath: dir,
29 | withIntermediateDirectories: true,
30 | attributes: nil)
31 | }
32 | }
33 | let string = Values.JSONString(value)
34 | try? string?.write(to: URL,
35 | atomically: atomically,
36 | encoding: .utf8)
37 | return string
38 | }
39 |
40 | public func read(_ type: T.Type,
41 | from file: String,
42 | encoding: String.Encoding = .utf8) -> T? {
43 | return read(type,
44 | from: URL(fileURLWithPath: file),
45 | encoding: encoding)
46 | }
47 |
48 | public func read(_ type: T.Type,
49 | from URL: URL,
50 | encoding: String.Encoding = .utf8) -> T? {
51 | guard let data = try? Data(contentsOf: URL) else { return nil }
52 |
53 | var value = JSONSerialization.kj_JSON(data, Any.self)
54 | if value == nil {
55 | value = String(data: data, encoding: encoding)
56 | }
57 | return Values.value(value, T.self) as? T
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Global/JSON.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JSON.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/2.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | // MARK: Model -> JSON
10 | public func JSONObject(from model: M) -> [String: Any] {
11 | return model.kj_JSONObject()
12 | }
13 |
14 | public func JSONObjectArray(from models: [M]) -> [[String: Any]] {
15 | return models.kj.JSONObjectArray()
16 | }
17 |
18 | //public func JSONArray(from value: [Any]) -> [Any] {
19 | // return value.kj.JSONArray()
20 | //}
21 | //
22 | //public func JSON(from value: Any) -> Any? {
23 | // return Values.JSONValue(value)
24 | //}
25 |
26 | public func JSONString(from value: Any,
27 | prettyPrinted: Bool = false) -> String {
28 | return Values.JSONString(value, prettyPrinted: prettyPrinted) ?? ""
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Global/Model.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Model.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/22.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - JSON -> Model
12 | public func model(from json: [String: Any],
13 | _ type: M.Type) -> M {
14 | return json.kj.model(type)
15 | }
16 |
17 | public func model(from json: [String: Any],
18 | type: Convertible.Type) -> Convertible {
19 | return json.kj.model(type: type)
20 | }
21 |
22 | public func model(from json: [NSString: Any],
23 | _ type: M.Type) -> M {
24 | return json.kj.model(type)
25 | }
26 |
27 | public func model(from json: [NSString: Any],
28 | type: Convertible.Type) -> Convertible {
29 | return json.kj.model(type: type)
30 | }
31 |
32 | public func model(from json: NSDictionary,
33 | _ type: M.Type) -> M? {
34 | return json.kj.model(type)
35 | }
36 |
37 | public func model(from json: NSDictionary,
38 | type: Convertible.Type) -> Convertible? {
39 | return json.kj.model(type: type)
40 | }
41 |
42 | public func model(from jsonString: String,
43 | _ type: M.Type) -> M? {
44 | return jsonString.kj.model(type)
45 | }
46 |
47 | public func model(from jsonString: String,
48 | type: Convertible.Type) -> Convertible? {
49 | return jsonString.kj.model(type: type)
50 | }
51 |
52 | public func model(from jsonString: NSString,
53 | _ type: M.Type) -> M? {
54 | return jsonString.kj.model(type)
55 | }
56 |
57 | public func model(from jsonString: NSString,
58 | type: Convertible.Type) -> Convertible? {
59 | return jsonString.kj.model(type: type)
60 | }
61 |
62 | public func model(from jsonData: Data,
63 | _ type: M.Type) -> M? {
64 | return jsonData.kj.model(type)
65 | }
66 |
67 | public func model(from jsonData: Data,
68 | type: Convertible.Type) -> Convertible? {
69 | return jsonData.kj.model(type: type)
70 | }
71 |
72 | public func model(from jsonData: NSData,
73 | _ type: M.Type) -> M? {
74 | return jsonData.kj.model(type)
75 | }
76 |
77 | public func model(from jsonData: NSData,
78 | type: Convertible.Type) -> Convertible? {
79 | return jsonData.kj.model(type: type)
80 | }
81 |
82 | public func modelArray(from json: [Any],
83 | _ type: M.Type) -> [M] {
84 | return json.kj.modelArray(type)
85 | }
86 |
87 | public func modelArray(from json: [Any],
88 | type: Convertible.Type) -> [Convertible] {
89 | return json.kj.modelArray(type: type)
90 | }
91 |
92 | public func modelArray(from json: NSArray,
93 | _ type: M.Type) -> [M] {
94 | return json.kj.modelArray(type)
95 | }
96 |
97 | public func modelArray(from json: NSArray,
98 | type: Convertible.Type) -> [Convertible] {
99 | return json.kj.modelArray(type: type)
100 | }
101 |
102 | public func modelArray(from jsonString: String,
103 | _ type: M.Type) -> [M] {
104 | return jsonString.kj.modelArray(type)
105 | }
106 |
107 | public func modelArray(from jsonString: String,
108 | type: Convertible.Type) -> [Convertible] {
109 | return jsonString.kj.modelArray(type: type)
110 | }
111 |
112 | public func modelArray(from jsonString: NSString,
113 | _ type: M.Type) -> [M] {
114 | return jsonString.kj.modelArray(type)
115 | }
116 |
117 | public func modelArray(from jsonString: NSString,
118 | type: Convertible.Type) -> [Convertible] {
119 | return jsonString.kj.modelArray(type: type)
120 | }
121 |
122 | public func modelArray(from jsonData: Data,
123 | _ type: M.Type) -> [M] {
124 | return jsonData.kj.modelArray(type)
125 | }
126 |
127 | public func modelArray(from jsonData: Data,
128 | type: Convertible.Type) -> [Convertible] {
129 | return jsonData.kj.modelArray(type: type)
130 | }
131 |
132 | public func modelArray(from jsonData: NSData,
133 | _ type: M.Type) -> [M] {
134 | return jsonData.kj.modelArray(type)
135 | }
136 |
137 | public func modelArray(from jsonData: NSData,
138 | type: Convertible.Type) -> [Convertible] {
139 | return jsonData.kj.modelArray(type: type)
140 | }
141 |
142 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Descriptor/ClassDescriptor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClassDescriptor.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/31.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | struct ClassDescriptor: ModelDescriptor {
10 | /// Flags describing the context, including its kind and format version
11 | let flags: ContextDescriptorFlags
12 |
13 | /// The parent context, or null if this is a top-level context.
14 | let parent: RelativeContextPointer
15 |
16 | /// The name of the type
17 | var name: RelativeDirectPointer
18 |
19 | /// A pointer to the metadata access function for this type
20 | let accessFunctionPtr: RelativeDirectPointer
21 |
22 | /// A pointer to the field descriptor for the type, if any
23 | var fields: RelativeDirectPointer
24 |
25 | /// The type of the superclass, expressed as a mangled type name that can refer to the generic arguments of the subclass type
26 | let superclassType: RelativeDirectPointer
27 |
28 | /// If this descriptor does not have a resilient superclass, this is the negative size of metadata objects of this class (in words)
29 | let metadataNegativeSizeInWords: UInt32
30 |
31 | /// If this descriptor does not have a resilient superclass, this is the positive size of metadata objects of this class (in words)
32 | let metadataPositiveSizeInWords: UInt32
33 |
34 | /// The number of additional members added by this class to the class metadata
35 | let numImmediateMembers: UInt32
36 |
37 | /// The number of stored properties in the class, not including its superclasses. If there is a field offset vector, this is its length.
38 | let numFields: UInt32
39 |
40 | /// The offset of the field offset vector for this class's stored properties in its metadata, in words. 0 means there is no field offset vector
41 | let fieldOffsetVectorOffset: FieldOffsetPointer
42 |
43 | let genericContextHeader: TargetTypeGenericContextDescriptorHeader
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Descriptor/Descriptor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Descriptor.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/31.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | /// Must be struct, do not use class
10 | /// Descriptor for layout
11 | protocol Descriptor {}
12 |
13 | // MARK: - NominalDescriptor
14 | protocol NominalDescriptor: Descriptor {
15 | /// Flags describing the context, including its kind and format version
16 | var flags: ContextDescriptorFlags { get }
17 |
18 | /// The parent context, or null if this is a top-level context.
19 | var parent: RelativeContextPointer { get }
20 |
21 | /// The name of the type
22 | var name: RelativeDirectPointer { get set }
23 |
24 | /// A pointer to the metadata access function for this type
25 | var accessFunctionPtr: RelativeDirectPointer { get }
26 |
27 | /// A pointer to the field descriptor for the type, if any
28 | var fields: RelativeDirectPointer { get set }
29 |
30 | associatedtype OffsetType: BinaryInteger
31 | var fieldOffsetVectorOffset: FieldOffsetPointer { get }
32 |
33 | /// generic info
34 | var genericContextHeader: TargetTypeGenericContextDescriptorHeader { get }
35 | }
36 |
37 | extension NominalDescriptor {
38 | var isGeneric: Bool { return (flags.value & 0x80) != 0 }
39 | var genericTypesCount: Int { return Int(genericContextHeader.base.numberOfParams) }
40 | }
41 |
42 | // MARK: - ModelDescriptor
43 | protocol ModelDescriptor: NominalDescriptor {
44 | var numFields: UInt32 { get }
45 | }
46 |
47 | extension ModelDescriptor {
48 | func fieldOffsets(_ type: Any.Type) -> [Int] {
49 | let ptr = ((type ~>> UnsafePointer.self) + Int(fieldOffsetVectorOffset.offset))
50 | .kj_raw ~> OffsetType.self
51 | return (0.. {
65 | var relativeOffset: Int32
66 |
67 | mutating func advanced() -> UnsafeMutablePointer {
68 | let offset = relativeOffset
69 | return withUnsafeMutablePointer(to: &self) {
70 | ($0.kj_raw + Int(offset)) ~> Pointee.self
71 | }
72 | }
73 | }
74 |
75 | struct FieldOffsetPointer {
76 | let offset: UInt32
77 | }
78 |
79 | struct MetadataResponse {}
80 |
81 | struct TargetTypeGenericContextDescriptorHeader {
82 | var instantiationCache: Int32
83 | var defaultInstantiationPattern: Int32
84 | var base: TargetGenericContextDescriptorHeader
85 | }
86 |
87 | struct TargetGenericContextDescriptorHeader {
88 | var numberOfParams: UInt16
89 | var numberOfRequirements: UInt16
90 | var numberOfKeyArguments: UInt16
91 | var numberOfExtraArguments: UInt16
92 | }
93 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Descriptor/EnumDescriptor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnumDescriptor.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/31.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | struct EnumDescriptor: NominalDescriptor {
10 | /// Flags describing the context, including its kind and format version
11 | let flags: ContextDescriptorFlags
12 |
13 | /// The parent context, or null if this is a top-level context.
14 | let parent: RelativeContextPointer
15 |
16 | /// The name of the type
17 | var name: RelativeDirectPointer
18 |
19 | /// A pointer to the metadata access function for this type
20 | let accessFunctionPtr: RelativeDirectPointer
21 |
22 | /// A pointer to the field descriptor for the type, if any
23 | var fields: RelativeDirectPointer
24 |
25 | /// The number of non-empty cases in the enum are in the low 24 bits; the offset of the payload size in the metadata record in words, if any, is stored in the high 8 bits
26 | let numPayloadCasesAndPayloadSizeOffset: UInt32
27 |
28 | /// The number of empty cases in the enum
29 | let numEmptyCases: UInt32
30 |
31 | let fieldOffsetVectorOffset: FieldOffsetPointer
32 |
33 | let genericContextHeader: TargetTypeGenericContextDescriptorHeader
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Descriptor/FieldDescriptor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FieldDescriptor.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/31.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct FieldRecord {
12 | let flags: Int32
13 | var _mangledTypeName: RelativeDirectPointer
14 | var _fieldName: RelativeDirectPointer
15 | var isVar: Bool { return (flags & 0x2) == 0x2 }
16 | mutating func fieldName() -> String { return String(cString: _fieldName.advanced()) }
17 | mutating func mangledTypeName() -> String { return String(cString: _mangledTypeName.advanced()) }
18 |
19 | mutating func type(_ genericContext: UnsafeRawPointer?,
20 | _ genericArguments: UnsafeRawPointer?) -> Any.Type {
21 | let name = _mangledTypeName.advanced()
22 | return _getTypeByMangledNameInContext(
23 | name,
24 | nameLength(name),
25 | genericContext,
26 | genericArguments
27 | )!
28 | }
29 |
30 | func nameLength(_ begin: UnsafeRawPointer) -> UInt {
31 | var end = begin
32 | let size = MemoryLayout.size
33 | while true {
34 | let cur = end.load(as: UInt8.self)
35 | if cur == 0 { break }
36 | end += 1
37 | if cur <= 0x17 {
38 | end += 4
39 | } else if cur <= 0x1F {
40 | end += size
41 | }
42 | }
43 | return UInt(end - begin)
44 | }
45 | }
46 |
47 | struct FieldDescriptor {
48 | let mangledTypeName: RelativeDirectPointer
49 | let superclass: RelativeDirectPointer
50 | let _kind : UInt16
51 | let fieldRecordSize : UInt16
52 | let numFields : UInt32
53 | var fieldRecords: FieldList
54 | var kind: FieldDescriptorKind { return FieldDescriptorKind(rawValue: _kind)! }
55 | }
56 |
57 | enum FieldDescriptorKind: UInt16 {
58 | case `struct`
59 | case `class`
60 | case `enum`
61 | case multiPayloadEnum
62 | case `protocol`
63 | case classProtocol
64 | case objCProtocol
65 | case objCClass
66 | }
67 |
68 | @_silgen_name("swift_getTypeByMangledNameInContext")
69 | private func _getTypeByMangledNameInContext(
70 | _ name: UnsafePointer,
71 | _ nameLength: UInt,
72 | _ genericContext: UnsafeRawPointer?,
73 | _ genericArguments: UnsafeRawPointer?)
74 | -> Any.Type?
75 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Descriptor/StructDescriptor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StructDescriptor.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/31.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | struct StructDescriptor: ModelDescriptor {
10 | /// Flags describing the context, including its kind and format version
11 | let flags: ContextDescriptorFlags
12 |
13 | /// The parent context, or null if this is a top-level context.
14 | let parent: RelativeContextPointer
15 |
16 | /// The name of the type
17 | var name: RelativeDirectPointer
18 |
19 | /// A pointer to the metadata access function for this type
20 | let accessFunctionPtr: RelativeDirectPointer
21 |
22 | /// A pointer to the field descriptor for the type, if any
23 | var fields: RelativeDirectPointer
24 |
25 | /// The number of stored properties in the struct. If there is a field offset vector, this is its length
26 | let numFields: UInt32
27 |
28 | /// The offset of the field offset vector for this struct's stored properties in its metadata, if any. 0 means there is no field offset vector
29 | let fieldOffsetVectorOffset: FieldOffsetPointer
30 |
31 | let genericContextHeader: TargetTypeGenericContextDescriptorHeader
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/LICENSE:
--------------------------------------------------------------------------------
1 | ## Type, Layout, Descriptor
2 |
3 | MIT License
4 |
5 | Copyright (c) 2017 Wesley Wickwire
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | SOFTWARE.
24 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Layout/BaseLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseLayout.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/2.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | struct BaseLayout: Layout {
10 | let kind: UnsafeRawPointer
11 | }
12 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Layout/ClassLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClassLayout.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/31.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | struct ClassLayout: ModelLayout {
10 | let kind: UnsafeRawPointer
11 | let superclass: Any.Type
12 |
13 | /// The cache data is used for certain dynamic lookups; it is owned by the runtime and generally needs to interoperate with Objective-C's use
14 | let runtimeReserved0: UInt
15 | let runtimeReserved1: UInt
16 |
17 | /// The data pointer is used for out-of-line metadata and is generally opaque, except that the compiler sets the low bit in order to indicate that this is a Swift metatype and therefore that the type metadata header is present
18 | let rodata: UInt
19 |
20 | /// Swift-specific class flags
21 | let flags: UInt32
22 |
23 | /// The address point of instances of this type
24 | let instanceAddressPoint: UInt32
25 |
26 | /// The required size of instances of this type. 'InstanceAddressPoint' bytes go before the address point; 'InstanceSize - InstanceAddressPoint' bytes go after it
27 | let instanceSize: UInt32
28 |
29 | /// The alignment mask of the address point of instances of this type
30 | let instanceAlignMask: UInt16
31 |
32 | /// Reserved for runtime use
33 | let reserved: UInt16
34 |
35 | /// The total size of the class object, including prefix and suffix extents
36 | let classSize: UInt32
37 |
38 | /// The offset of the address point within the class object
39 | let classAddressPoint: UInt32
40 |
41 | // Description is by far the most likely field for a client to try to access directly, so we force access to go through accessors
42 | /// An out-of-line Swift-specific description of the type, or null if this is an artificial subclass. We currently provide no supported mechanism for making a non-artificial subclass dynamically
43 | var description: UnsafeMutablePointer
44 |
45 | /// A function for destroying instance variables, used to clean up after an early return from a constructor. If null, no clean up will be performed and all ivars must be trivial
46 | let iVarDestroyer: UnsafeRawPointer
47 |
48 | var genericTypeOffset: Int {
49 | let descriptor = description.pointee
50 | // don't have resilient superclass
51 | if (0x4000 & flags) == 0 {
52 | return (flags & 0x800) == 0
53 | ? Int(descriptor.metadataPositiveSizeInWords - descriptor.numImmediateMembers)
54 | : -Int(descriptor.metadataNegativeSizeInWords)
55 | }
56 | return GenenicTypeOffset.wrong
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Layout/EnumLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnumLayout.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/31.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | struct EnumLayout: NominalLayout {
10 | let kind: UnsafeRawPointer
11 | /// An out-of-line description of the type
12 | var description: UnsafeMutablePointer
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Layout/FieldList.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FieldList.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/1.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | struct FieldList- {
10 | private let item: Item
11 | mutating func ptr(_ index: Int) -> UnsafeMutablePointer
- {
12 | return withUnsafeMutablePointer(to: &self) {
13 | ($0 + index).kj_raw ~> Item.self
14 | }
15 | }
16 |
17 | mutating func item(_ index: Int) -> Item {
18 | return withUnsafeMutablePointer(to: &self) {
19 | $0[index].item
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Layout/FunctionLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionLayout.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/1.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | struct FunctionLayout: Layout {
10 | let kind: UnsafeRawPointer
11 | var flags: Int
12 | var parameters: FieldList
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Layout/Layout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Layout.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/31.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | /// Must be struct, do not use class
10 | /// Memory layout for metadata
11 | protocol Layout {
12 | // Only valid for non-class metadata
13 | var kind: UnsafeRawPointer { get }
14 | }
15 |
16 | protocol NominalLayout: Layout {
17 | associatedtype DescriptorType: NominalDescriptor
18 | var description: UnsafeMutablePointer { get }
19 | var genericTypeOffset: Int { get }
20 | }
21 |
22 | extension NominalLayout {
23 | var genericTypeOffset: Int { return 2 }
24 | }
25 |
26 | protocol ModelLayout: NominalLayout where DescriptorType: ModelDescriptor {}
27 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Layout/ProtocolLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProtocolLayout.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/1.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | struct ProtocolLayout: Layout {
10 | let kind: UnsafeRawPointer
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Layout/StructLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StructLayout.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/31.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | struct StructLayout: ModelLayout {
10 | let kind: UnsafeRawPointer
11 | /// An out-of-line description of the type
12 | var description: UnsafeMutablePointer
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Layout/TupleLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TupleLayout.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/1.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | struct TupleLayout: Layout {
10 | let kind: UnsafeRawPointer
11 | let numElements: Int
12 | let labels: UnsafeMutablePointer
13 | var elements: FieldList
14 | }
15 |
16 | struct TupleElement {
17 | let type: Any.Type
18 | let offset: Int
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Metadata.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Metadata.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/4.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct Metadata {
12 | private static let typeLock = NSRecursiveLock()
13 | private static var types = [TypeKey: BaseType]()
14 |
15 | public static func type(_ type: Any.Type) -> BaseType? {
16 | typeLock.lock()
17 | defer { typeLock.unlock() }
18 |
19 | // get from cache
20 | let key = typeKey(type)
21 | if let mt = types[key] { return mt }
22 |
23 | // name
24 | let name = String(describing: type)
25 | if name == "Swift._SwiftObject"
26 | || name == "NSObject"
27 | || name == "_TtCs12_SwiftObject" { return nil }
28 |
29 | // type judge
30 | var mtt: BaseType.Type
31 | let kind = Kind(type)
32 | switch kind {
33 | case .class: mtt = ClassType.self
34 | case .struct: mtt = StructType.self
35 | case .enum: mtt = EnumType.self
36 | case .optional: mtt = OptionalType.self
37 | case .objCClassWrapper: mtt = ObjCClassType.self
38 | case .foreignClass: mtt = ForeignClassType.self
39 | case .tuple: mtt = TupleType.self
40 | case .function: mtt = FunctionType.self
41 | case .existential: mtt = ProtocolType.self
42 | case .metatype: mtt = MetaType.self
43 | default: mtt = BaseType.self
44 | }
45 |
46 | // ceate and put it into cache
47 | let mt = mtt.init(name: name, type: type, kind: kind)
48 | types[key] = mt
49 | return mt
50 | }
51 |
52 | public static func type(_ obj: Any) -> BaseType? {
53 | return type(Swift.type(of: obj))
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Property.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Property.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/30.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | /// Info for stored property(存储属性的相关信息)
10 | public class Property: CustomStringConvertible {
11 | public let name: String
12 | public let type: Any.Type
13 | public private(set) lazy var dataType: Any.Type = type~!
14 | public let isVar: Bool
15 | public let offset: Int
16 | public let ownerType: Any.Type
17 |
18 | init(name: String, type: Any.Type,
19 | isVar: Bool, offset: Int,
20 | ownerType: Any.Type) {
21 | self.name = name
22 | self.type = type
23 | self.isVar = isVar
24 | self.offset = offset
25 | self.ownerType = ownerType
26 | }
27 |
28 | func set(_ value: Any, for model: UnsafeMutableRawPointer) {
29 | (model + offset).kj_set(value, type)
30 | }
31 |
32 | func get(from model: UnsafeMutableRawPointer) -> Any {
33 | return (model + offset).kj_get(type)
34 | }
35 |
36 | public var description: String {
37 | return "\(name) { type = \(type), isVar = \(isVar), offset = \(offset), ownerType = \(ownerType) }"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Type/BaseType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseType.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/30.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | /// Info for type(类型的相关信息)
10 | public class BaseType: Type, CustomStringConvertible {
11 | public let name: String
12 | public let type: Any.Type
13 | public let kind: Kind
14 | public private(set) var module: String = ""
15 | public private(set) var isSystemModule: Bool = false
16 |
17 | required init(name: String, type: Any.Type, kind: Kind) {
18 | self.name = name
19 | self.type = type
20 | self.kind = kind
21 |
22 | build()
23 | }
24 |
25 | func build() {
26 | switch kind {
27 | case .optional, .struct, .class, .existential,
28 | .enum, .objCClassWrapper, .foreignClass:
29 | let name = String(reflecting: type)
30 | guard let end = name.firstIndex(of: ".") else { break }
31 | module = String(name[name.startIndex..!
11 | public private(set) var `super`: ClassType?
12 | // public private(set) var isPureSwiftClass: Bool = false
13 |
14 | override func build() {
15 | super.build()
16 |
17 | layout = builtLayout()
18 | genericTypes = builtGenericTypes()
19 | properties = builtProperties()
20 |
21 | `super` = Metadata.type(layout.pointee.superclass) as? ClassType
22 | if let superProperties = `super`?.properties {
23 | properties = properties ?? []
24 | properties!.insert(contentsOf: superProperties, at: 0)
25 | }
26 |
27 | /// Not sure
28 | // isPureSwiftClass = (layout.pointee.rodata ~>> UnsafePointer.self).pointee > 0
29 | }
30 |
31 | override public var description: String {
32 | return "\(name) { kind = \(kind), properties = \(properties ?? []), genericTypes = \(genericTypes ?? []), super = \(`super` != nil ? String(describing: `super`!) : "nil") }"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Type/EnumType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnumType.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/31.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | public class EnumType: BaseType, NominalType, LayoutType {
10 | private(set) var layout: UnsafeMutablePointer!
11 | public private(set) var genericTypes: [Any.Type]?
12 | public private(set) var cases: [String]?
13 |
14 | override func build() {
15 | super.build()
16 |
17 | layout = builtLayout()
18 | genericTypes = builtGenericTypes()
19 |
20 | let description = layout.pointee.description
21 | let count = Int(description.pointee.numEmptyCases)
22 | guard count > 0 else { return }
23 | let descriptor = description.pointee.fields.advanced()
24 | cases = (0..!
12 |
13 | override func build() {
14 | super.build()
15 |
16 | layout = builtLayout()
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Type/FunctionType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionType.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/1.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | public class FunctionType: BaseType, LayoutType {
10 | private(set) var layout: UnsafeMutablePointer!
11 | public private(set) var `throws`: Bool = false
12 | public private(set) var returnType: Any.Type = Any.self
13 | public private(set) var argumentTypes: [Any.Type]?
14 |
15 | override func build() {
16 | super.build()
17 |
18 | layout = builtLayout()
19 |
20 | `throws` = (layout.pointee.flags & 0x01000000) != 0
21 | returnType = layout.pointee.parameters.item(0)
22 |
23 | let argumentsCount = layout.pointee.flags & 0x00FFFFFF
24 | guard argumentsCount > 0 else { return }
25 | var arr = [Any.Type]()
26 | for i in 1...argumentsCount {
27 | arr.append(layout.pointee.parameters.item(i))
28 | }
29 | argumentTypes = arr
30 | }
31 |
32 | override public var description: String {
33 | return "\(name) { kind = \(kind), argumentTypes = \(argumentTypes ?? []), returnType = \(returnType), throws = \(`throws`) }"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Type/Kind.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Kind.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/30.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | public enum Kind {
10 | // MARK: - cases
11 |
12 | case `class`
13 | /// e.g. Int、String
14 | case `struct`
15 | /// e.g. MemoryLayout
16 | case `enum`
17 | /// e.g. Optional、T?
18 | case optional
19 | /// Such as a Core Foundation class, e.g. CFArray
20 | case foreignClass
21 | /// A type whose value is not exposed in the metadata system
22 | case opaque
23 | case tuple
24 | /// A monomorphic function, e.g. () -> Void
25 | case function
26 | /// An existential type, e.g. protocol
27 | case existential
28 | /// A metatype
29 | case metatype
30 | /// An ObjC class wrapper, e.g. NSString
31 | case objCClassWrapper
32 | /// An existential metatype
33 | case existentialMetatype
34 | /// A heap-allocated local variable using statically-generated metadata
35 | case heapLocalVariable
36 | case heapArray
37 | /// A heap-allocated local variable using runtime-instantiated metadata.
38 | case heapGenericLocalVariable
39 | /// A native error object.
40 | case errorObject
41 |
42 | // MARK: - Some flags
43 | /// Non-type metadata kinds have this bit set
44 | private static let nonType: UInt = 0x400
45 | /// Non-heap metadata kinds have this bit set
46 | private static let nonHeap: UInt = 0x200
47 | /*
48 | The above two flags are negative because the "class" kind has to be zero, and class metadata is both type and heap metadata.
49 | */
50 | /// Runtime-private metadata has this bit set. The compiler must not statically generate metadata objects with these kinds, and external tools should not rely on the stability of these values or the precise binary layout of their associated data structures
51 | private static let runtimePrivate: UInt = 0x100
52 | private static let runtimePrivate_nonHeap = runtimePrivate | nonHeap
53 | private static let runtimePrivate_nonType = runtimePrivate | nonType
54 |
55 | // MARK: - initialization
56 |
57 | /// 获得Any.Type对应的MetadataKind
58 | init(_ type: Any.Type) {
59 | let kind = (type ~>> UnsafePointer.self).pointee
60 |
61 | switch kind {
62 | case 0 | Kind.nonHeap, 1: self = .struct
63 | case 1 | Kind.nonHeap, 2: self = .enum
64 | case 2 | Kind.nonHeap, 3: self = .optional
65 | case 3 | Kind.nonHeap: self = .foreignClass
66 |
67 | case 0 | Kind.runtimePrivate_nonHeap, 8: self = .opaque
68 | case 1 | Kind.runtimePrivate_nonHeap, 9: self = .tuple
69 | case 2 | Kind.runtimePrivate_nonHeap, 10: self = .function
70 | case 3 | Kind.runtimePrivate_nonHeap, 12: self = .existential
71 | case 4 | Kind.runtimePrivate_nonHeap, 13: self = .metatype
72 | case 5 | Kind.runtimePrivate_nonHeap, 14: self = .objCClassWrapper
73 | case 6 | Kind.runtimePrivate_nonHeap, 15: self = .existentialMetatype
74 |
75 | case 0 | Kind.nonType, 64: self = .heapLocalVariable
76 | case 0 | Kind.runtimePrivate_nonType: self = .heapGenericLocalVariable
77 | case 1 | Kind.runtimePrivate_nonType: self = .errorObject
78 |
79 | case 65: self = .heapArray
80 |
81 | case 0: fallthrough
82 | default: self = .class
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Type/MetaType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MetaType.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/2.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | public class MetaType: BaseType, LayoutType {
10 | private(set) var layout: UnsafeMutablePointer!
11 |
12 | override func build() {
13 | super.build()
14 |
15 | layout = builtLayout()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Type/ModelType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ModelType.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/13.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class ModelType: BaseType {
12 | public internal(set) var properties: [Property]?
13 | public internal(set) var genericTypes: [Any.Type]?
14 | private var modelKeysLock = DispatchSemaphore(value: 1)
15 | private var modelKeys: [String: ModelPropertyKey] = [:]
16 | private var jsonKeysLock = DispatchSemaphore(value: 1)
17 | private var jsonKeys: [String: String] = [:]
18 |
19 | func modelKey(from propertyName: String,
20 | _ createdKey: @autoclosure () -> ModelPropertyKey) -> ModelPropertyKey {
21 | modelKeysLock.wait()
22 | defer { modelKeysLock.signal() }
23 | if let key = modelKeys[propertyName] { return key }
24 |
25 | var resultKey = createdKey()
26 | if let stringKey = resultKey as? String {
27 | if stringKey != propertyName {
28 | resultKey = [stringKey, propertyName]
29 | }
30 | } else if var arrayKey = resultKey as? [String] {
31 | if arrayKey.count == 0 {
32 | resultKey = propertyName
33 | } else if arrayKey.last != propertyName {
34 | arrayKey.append(propertyName)
35 | resultKey = arrayKey
36 | }
37 | }
38 | modelKeys[propertyName] = resultKey;
39 | return resultKey
40 | }
41 |
42 | func JSONKey(from propertyName: String,
43 | _ createdKey: @autoclosure () -> String) -> String {
44 | jsonKeysLock.wait()
45 | defer { jsonKeysLock.signal() }
46 | if let key = jsonKeys[propertyName] { return key }
47 |
48 | let resultKey = createdKey()
49 | jsonKeys[propertyName] = resultKey
50 | return resultKey
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Type/ObjCClassType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ObjCClassType.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/2.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | /// e.g. NSString
10 | public class ObjCClassType: BaseType, LayoutType {
11 | private(set) var layout: UnsafeMutablePointer!
12 |
13 | override func build() {
14 | super.build()
15 |
16 | layout = builtLayout()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Type/OptionalType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OptionalType.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/2.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | /// Optional metadata share the same basic layout as enum metadata
10 | public class OptionalType: EnumType {
11 | public private(set) var wrapType: Any.Type = Any.self
12 |
13 | override func build() {
14 | super.build()
15 |
16 | var wt: BaseType! = self
17 | while wt.kind == .optional {
18 | wt = Metadata.type((wt as! OptionalType).genericTypes![0])
19 | }
20 | wrapType = wt.type
21 | }
22 |
23 | override public var description: String {
24 | return "\(name) { kind = \(kind), wrapType = \(wrapType) }"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Type/ProtocolType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProtocolType.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/1.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | public class ProtocolType: BaseType, LayoutType {
10 | private(set) var layout: UnsafeMutablePointer!
11 |
12 | override func build() {
13 | super.build()
14 |
15 | layout = builtLayout()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Type/StructType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StructType.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/7/31.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | public class StructType: ModelType, PropertyType, LayoutType {
10 | private(set) var layout: UnsafeMutablePointer!
11 |
12 | override func build() {
13 | super.build()
14 |
15 | layout = builtLayout()
16 | properties = builtProperties()
17 | genericTypes = builtGenericTypes()
18 | }
19 |
20 | override public var description: String {
21 | return "\(name) { kind = \(kind), properties = \(properties ?? []), genericTypes = \(genericTypes ?? []) }"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/KakaJSON/Metadata/Type/TupleType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TupleType.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2019/8/1.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | public class TupleType: BaseType, LayoutType {
10 | private(set) var layout: UnsafeMutablePointer!
11 | public private(set) var properties: [Property]!
12 |
13 | override func build() {
14 | super.build()
15 |
16 | layout = builtLayout()
17 |
18 | let elementsCount = layout.pointee.numElements
19 | guard elementsCount > 0 else { return }
20 | var names: [String]
21 | if layout.pointee.labels ~>> Int.self == 0 {
22 | names = Array(repeating: "", count: elementsCount)
23 | } else {
24 | names = String(cString: layout.pointee.labels).components(separatedBy: " ")
25 | }
26 | properties = (0..! { get }
22 | }
23 |
24 | extension LayoutType {
25 | func builtLayout() -> UnsafeMutablePointer {
26 | return type ~>> UnsafeMutablePointer.self
27 | }
28 | }
29 |
30 | // MARK: - NominalType
31 | protocol NominalType: Type {
32 | var genericTypes: [Any.Type]? { get }
33 | }
34 |
35 | extension NominalType where Self: LayoutType, InnerLayout: NominalLayout {
36 | func genenicTypesPtr() -> UnsafeMutablePointer> {
37 | // get the offset
38 | let offset = layout.pointee.genericTypeOffset * MemoryLayout.size
39 |
40 | // pointer to generic types
41 | return ((type ~>> UnsafeMutableRawPointer.self) + offset) ~> FieldList.self
42 | }
43 |
44 | func builtGenericTypes() -> [Any.Type]? {
45 | // generic judge
46 | let description = layout.pointee.description
47 | if !description.pointee.isGeneric { return nil }
48 | let typesCount = description.pointee.genericTypesCount
49 | if typesCount <= 0 { return nil }
50 |
51 | // pointer to generic types
52 | if layout.pointee.genericTypeOffset == GenenicTypeOffset.wrong { return nil }
53 | let ptr = genenicTypesPtr()
54 | return (0.. [Property]? {
69 | let description = layout.pointee.description
70 | let offsets = description.pointee.fieldOffsets(type)
71 | if offsets.isEmpty { return nil }
72 | let fieldDescriptorPtr = description.pointee.fields.advanced()
73 | let ptr = genenicTypesPtr()
74 | return (0.. Property in
75 | let recordPtr = fieldDescriptorPtr.pointee.fieldRecords.ptr(i)
76 | return Property(name: recordPtr.pointee.fieldName(),
77 | type: recordPtr.pointee.type(layout.pointee.description, ptr),
78 | isVar: recordPtr.pointee.isVar,
79 | offset: offsets[i],
80 | ownerType: type)
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/JSON_To_Model/JTM_01_Basic.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JTM_01_Basic.swift
3 | // KakaJSONTest
4 | //
5 | // Created by MJ Lee on 2019/8/7.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import KakaJSON
11 |
12 | class JTM_01_Basic: XCTestCase {
13 | struct Cat: Convertible {
14 | var weight: Double = 0.0
15 | var name: String = ""
16 | }
17 |
18 | // MARK: - Generic Type
19 | func testGeneric() {
20 | let name = "Miaomiao"
21 | let weight = 6.66
22 |
23 | // json can also be NSDictionary\NSMutableDictionary
24 | let json: [String: Any] = [
25 | "weight": weight,
26 | "name": name
27 | ]
28 |
29 | let cat = json.kj.model(Cat.self)
30 | XCTAssert(cat.name == name)
31 | XCTAssert(cat.weight == weight)
32 | }
33 |
34 | // MARK: - Any.Type
35 | func testAny() {
36 | let name: String = "Miaomiao"
37 | let weight: Double = 6.66
38 |
39 | let json: [String: Any] = [
40 | "weight": weight,
41 | "name": name
42 | ]
43 |
44 | let type: Convertible.Type = Cat.self
45 | let cat = json.kj.model(type: type) as? Cat
46 | XCTAssert(cat?.name == name)
47 | XCTAssert(cat?.weight == weight)
48 | }
49 |
50 | // MARK: - json String
51 | func testJSONString() {
52 | let name = "Miaomiao"
53 | let weight = 6.66
54 |
55 | // jsonString can also be NSString\NSMutableString
56 | let jsonString: String = """
57 | {
58 | "name": "\(name)",
59 | "weight": \(weight)
60 | }
61 | """
62 |
63 | let cat = jsonString.kj.model(Cat.self)
64 | XCTAssert(cat?.name == name)
65 | XCTAssert(cat?.weight == weight)
66 | }
67 |
68 | // MARK: - json data
69 | func testJSONData() {
70 | let name = "Miaomiao"
71 | let weight = 6.66
72 |
73 | // jsonData can also be NSData\NSMutableData
74 | let jsonData = """
75 | {
76 | "name": "\(name)",
77 | "weight": \(weight)
78 | }
79 | """.data(using: .utf8)!
80 |
81 | let cat = jsonData.kj.model(Cat.self)
82 | XCTAssert(cat?.name == name)
83 | XCTAssert(cat?.weight == weight)
84 | }
85 |
86 | // MARK: - NSNull
87 | func testNSNull() {
88 | struct Cat: Convertible {
89 | var weight: Double = 0.0
90 | var name: String = "xx"
91 | var data: NSNull?
92 | }
93 |
94 | let json: [String: Any] = [
95 | "name": NSNull(),
96 | "weight": 6.6,
97 | "data": NSNull()
98 | ]
99 |
100 | let cat = json.kj.model(Cat.self)
101 | // convert failed, keep default value
102 | XCTAssert(cat.name == "xx")
103 | XCTAssert(cat.weight == 6.6)
104 | XCTAssert(cat.data == NSNull())
105 | }
106 |
107 | // MARK: - let
108 | func testLet() {
109 | struct Cat: Convertible {
110 | // let of integer type is very restricted in release mode
111 | // please user `private(set) var` instead of `let`
112 | private(set) var weight: Double = 0.0
113 | let name: String = ""
114 | }
115 | let name: String = "Miaomiao"
116 | let weight: Double = 6.66
117 |
118 | let json: [String: Any] = [
119 | "weight": weight,
120 | "name": name
121 | ]
122 |
123 | let cat = json.kj.model(Cat.self)
124 | XCTAssert(cat.name == name)
125 | XCTAssert(cat.weight == weight)
126 | }
127 |
128 | // MARK: - Class Type
129 | func testClass() {
130 | class Person: Convertible {
131 | var name: String = ""
132 | var age: Int = 0
133 | required init() {}
134 | }
135 |
136 | class Student: Person {
137 | var score: Int = 0
138 | var no: String = ""
139 | }
140 |
141 | let name = "jack"
142 | let age = 18
143 | let score = 98
144 | let no = "9527"
145 |
146 | let json: [String: Any] = [
147 | "name": name,
148 | "age": age,
149 | "score": score,
150 | "no": no
151 | ]
152 |
153 | let student = json.kj.model(Student.self)
154 | XCTAssert(student.name == name)
155 | XCTAssert(student.age == age)
156 | XCTAssert(student.score == score)
157 | XCTAssert(student.no == no)
158 | }
159 |
160 | // MARK: - NSObject Class Type
161 | func testNSObjectClass() {
162 | class Person: NSObject, Convertible {
163 | var name: String = ""
164 | var age: Int = 0
165 | required override init() {}
166 | }
167 |
168 | class Student: Person {
169 | var score: Int = 0
170 | var no: String = ""
171 | }
172 |
173 | let name = "jack"
174 | let age = 18
175 | let score = 98
176 | let no = "9527"
177 |
178 | let json: [String: Any] = [
179 | "name": name,
180 | "age": age,
181 | "score": score,
182 | "no": no
183 | ]
184 |
185 | let student = json.kj.model(Student.self)
186 | XCTAssert(student.name == name)
187 | XCTAssert(student.age == age)
188 | XCTAssert(student.score == score)
189 | XCTAssert(student.no == no)
190 | }
191 |
192 | // MARK: - Convert
193 | func testConvert() {
194 | let name = "Miaomiao"
195 | let weight = 6.66
196 |
197 | let json: [String: Any] = [
198 | "weight": weight,
199 | "name": name
200 | ]
201 |
202 | var cat = Cat()
203 | cat.kj_m.convert(from: json)
204 | XCTAssert(cat.name == name)
205 | XCTAssert(cat.weight == weight)
206 |
207 | let JSONString = """
208 | {
209 | "weight": 6.66,
210 | "name": "Miaomiao"
211 | }
212 | """
213 |
214 | cat.kj_m.convert(from: JSONString)
215 | XCTAssert(cat.name == name)
216 | XCTAssert(cat.weight == weight)
217 | }
218 |
219 | func testCallback1() {
220 | struct Car: Convertible {
221 | var name: String = ""
222 | var age: Int = 0
223 |
224 | mutating func kj_willConvertToModel(from json: [String: Any]) {
225 | // print("Car - kj_willConvertToModel")
226 | }
227 |
228 | mutating func kj_didConvertToModel(from json: [String: Any]) {
229 | // print("Car - kj_didConvertToModel")
230 | }
231 | }
232 |
233 | let name = "Benz"
234 | let age = 100
235 | let car = ["name": name, "age": age].kj.model(Car.self)
236 | // Car - kj_willConvertToModel
237 | // Car - kj_didConvertToModel
238 | XCTAssert(car.name == name)
239 | XCTAssert(car.age == age)
240 | }
241 |
242 | func testCallback2() {
243 | class Person: Convertible {
244 | var name: String = ""
245 | var age: Int = 0
246 | required init() {}
247 |
248 | func kj_willConvertToModel(from json: [String: Any]) {
249 | // print("Person - kj_willConvertToModel")
250 | }
251 |
252 | func kj_didConvertToModel(from json: [String: Any]) {
253 | // print("Person - kj_didConvertToModel")
254 | }
255 | }
256 |
257 | class Student: Person {
258 | var score: Int = 0
259 |
260 | override func kj_willConvertToModel(from json: [String: Any]) {
261 | // call super's implementation if necessary
262 | super.kj_willConvertToModel(from: json)
263 |
264 | // print("Student - kj_willConvertToModel")
265 | }
266 |
267 | override func kj_didConvertToModel(from json: [String: Any]) {
268 | // call super's implementation if necessary
269 | super.kj_didConvertToModel(from: json)
270 |
271 | // print("Student - kj_didConvertToModel")
272 | }
273 | }
274 |
275 | let name = "jack"
276 | let age = 10
277 | let score = 100
278 | let student = ["name": name, "age": age, "score": score].kj.model(Student.self)
279 | // Person - kj_willConvertToModel
280 | // Student - kj_willConvertToModel
281 | // Person - kj_didConvertToModel
282 | // Student - kj_didConvertToModel
283 | XCTAssert(student.name == name)
284 | XCTAssert(student.age == age)
285 | XCTAssert(student.score == score)
286 | }
287 |
288 | func testOCModel() {
289 | // Not support model extends pure oc model
290 | final class Person: OCModel, Convertible {
291 | var name: String = ""
292 | var age: Int = 0
293 | }
294 |
295 | /*
296 | let name = "Jack"
297 | let age = 20
298 | let no = 1
299 | let json: [String: Any] = [
300 | "no": no,
301 | "name": name,
302 | "age": age
303 | ]
304 |
305 |
306 | let person = json.kj.model(Person.self)
307 | XCTAssert(person.name == name)
308 | XCTAssert(person.age == age)
309 | XCTAssert(person.no == no)
310 | */
311 | }
312 |
313 | static var allTests = [
314 | "testGeneric": testGeneric,
315 | "testAny": testAny,
316 | "testJSONString": testJSONString,
317 | "testJSONData": testJSONData,
318 | "testNSNull": testNSNull,
319 | "testLet": testLet,
320 | "testClass": testClass,
321 | "testNSObjectClass": testNSObjectClass,
322 | "testConvert": testConvert,
323 | "testCallback1": testCallback1,
324 | "testCallback2": testCallback2,
325 | "testOCModel": testOCModel
326 | ]
327 | }
328 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/JSON_To_Model/JTM_03_NestedModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JTM_03_NestedModel.swift
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/8/10.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import KakaJSON
11 |
12 | class JTM_03_NestedModel: XCTestCase {
13 | func testNormal() {
14 | struct Book: Convertible {
15 | var name: String = ""
16 | var price: Double = 0.0
17 | }
18 |
19 | struct Car: Convertible {
20 | var name: String = ""
21 | var price: Double = 0.0
22 | }
23 |
24 | struct Dog: Convertible {
25 | var name: String = ""
26 | var age: Int = 0
27 | }
28 |
29 | struct Person: Convertible {
30 | var name: String = ""
31 | var car: Car?
32 | var books: [Book]?
33 | var dogs: [String: Dog]?
34 | }
35 |
36 | let name = "Jack"
37 | let car = (name: "BMW7", price: 109.5)
38 | let books = [
39 | (name: "Fast C++", price: 666),
40 | (name: "Data Structure And Algorithm", price: 1666)
41 | ]
42 | let dogs = [
43 | (name: "Larry", age: 5),
44 | (name: "Wangwang", age: 2)
45 | ]
46 |
47 | let json: [String: Any] = [
48 | "name": name,
49 | "car": ["name": car.name, "price": car.price],
50 | "books": [
51 | ["name": books[0].name, "price": books[0].price],
52 | ["name": books[1].name, "price": books[1].price]
53 | ],
54 | "dogs": [
55 | "dog0": ["name": dogs[0].name, "age": dogs[0].age],
56 | "dog1": ["name": dogs[1].name, "age": dogs[1].age]
57 | ]
58 | ]
59 |
60 | let person = json.kj.model(Person.self)
61 | XCTAssert(person.name == name)
62 |
63 | XCTAssert(person.car?.name == car.name)
64 | XCTAssert(person.car?.price == car.price)
65 |
66 | XCTAssert(person.books?.count == books.count)
67 | XCTAssert(person.books?[0].name == books[0].name)
68 | XCTAssert(person.books?[0].price == Double(books[0].price))
69 | XCTAssert(person.books?[1].name == books[1].name)
70 | XCTAssert(person.books?[1].price == Double(books[1].price))
71 |
72 | XCTAssert(person.dogs?.count == dogs.count)
73 | XCTAssert(person.dogs?["dog0"]?.name == dogs[0].name)
74 | XCTAssert(person.dogs?["dog0"]?.age == dogs[0].age)
75 | XCTAssert(person.dogs?["dog1"]?.name == dogs[1].name)
76 | XCTAssert(person.dogs?["dog1"]?.age == dogs[1].age)
77 | }
78 |
79 | func testDefaultValue() {
80 | struct Car: Convertible {
81 | var name: String = ""
82 | var price: Double = 0.0
83 | }
84 |
85 | class Dog: Convertible {
86 | var name: String = ""
87 | var age: Int = 0
88 | required init() {}
89 | init(name: String, age: Int) {
90 | self.name = name
91 | self.age = age
92 | }
93 | }
94 |
95 | struct Person: Convertible {
96 | var name: String = ""
97 | // KakaJSON will use your defaultValue instead of creating a new model
98 | // KakaJSON will not creat a new model again if you already have a default model value
99 | var car: Car = Car(name: "Bently", price: 106.5)
100 | var dog: Dog = Dog(name: "Larry", age: 5)
101 | }
102 |
103 | let json: [String: Any] = [
104 | "name": "Jake",
105 | "car": ["price": 305.6],
106 | "dog": ["name": "Wangwang"]
107 | ]
108 |
109 | let person = json.kj.model(Person.self)
110 | XCTAssert(person.name == "Jake")
111 | // keep defaultValue
112 | XCTAssert(person.car.name == "Bently")
113 | // use value from json
114 | XCTAssert(person.car.price == 305.6)
115 | // use value from json
116 | XCTAssert(person.dog.name == "Wangwang")
117 | // keep defaultValue
118 | XCTAssert(person.dog.age == 5)
119 | }
120 |
121 | func testRecursive() {
122 | class Person: Convertible {
123 | var name: String = ""
124 | var parent: Person?
125 | required init() {}
126 | }
127 |
128 | let json: [String: Any] = [
129 | "name": "Jack",
130 | "parent": ["name": "Jim"]
131 | ]
132 |
133 | let person = json.kj.model(Person.self)
134 | XCTAssert(person.name == "Jack")
135 | XCTAssert(person.parent?.name == "Jim")
136 | }
137 |
138 | func testGeneric() {
139 | struct User: Convertible {
140 | let id: String = ""
141 | let nickName: String = ""
142 | }
143 |
144 | struct Goods: Convertible {
145 | private(set) var price: CGFloat = 0.0
146 | let name: String = ""
147 | }
148 |
149 | struct NetResponse: Convertible {
150 | let data: Element? = nil
151 | let msg: String = ""
152 | private(set) var code: Int = 0
153 | }
154 |
155 | let json1 = """
156 | {
157 | "data": {"nickName": "KaKa", "id": 213234234},
158 | "msg": "Success",
159 | "code" : 200
160 | }
161 | """
162 | let response1 = json1.kj.model(NetResponse.self)
163 | XCTAssert(response1?.msg == "Success")
164 | XCTAssert(response1?.code == 200)
165 | XCTAssert(response1?.data?.nickName == "KaKa")
166 | XCTAssert(response1?.data?.id == "213234234")
167 |
168 | let json2 = """
169 | {
170 | "data": [
171 | {"price": "6199", "name": "iPhone XR"},
172 | {"price": "8199", "name": "iPhone XS"},
173 | {"price": "9099", "name": "iPhone Max"}
174 | ],
175 | "msg": "Success",
176 | "code" : 200
177 | }
178 | """
179 | let response2 = json2.kj.model(NetResponse<[Goods]>.self)
180 | XCTAssert(response2?.msg == "Success")
181 | XCTAssert(response2?.code == 200)
182 | XCTAssert(response2?.data?.count == 3)
183 | XCTAssert(response2?.data?[0].price == 6199)
184 | XCTAssert(response2?.data?[0].name == "iPhone XR")
185 | XCTAssert(response2?.data?[1].price == 8199)
186 | XCTAssert(response2?.data?[1].name == "iPhone XS")
187 | XCTAssert(response2?.data?[2].price == 9099)
188 | XCTAssert(response2?.data?[2].name == "iPhone Max")
189 | }
190 |
191 | func testOptional() {
192 | struct Book: Convertible {
193 | var name: String = ""
194 | var price: Double = 0.0
195 | }
196 |
197 | struct Dog: Convertible {
198 | var name: String = ""
199 | var age: Int = 0
200 | }
201 |
202 | struct Person: Convertible {
203 | var name: String = ""
204 | var books: [Book??]?
205 | var dogs: [String: Dog???]?
206 | }
207 |
208 | let name = "Jack"
209 | let books = [
210 | (name: "Fast C++", price: 666),
211 | (name: "Data Structure And Algorithm", price: 1666)
212 | ]
213 | let dogs = [
214 | (name: "Larry", age: 5),
215 | (name: "Wangwang", age: 2)
216 | ]
217 |
218 | let json: [String: Any] = [
219 | "name": name,
220 | "books": [
221 | ["name": books[0].name, "price": books[0].price],
222 | ["name": books[1].name, "price": books[1].price]
223 | ],
224 | "dogs": [
225 | "dog0": ["name": dogs[0].name, "age": dogs[0].age],
226 | "dog1": ["name": dogs[1].name, "age": dogs[1].age]
227 | ]
228 | ]
229 |
230 | let person = json.kj.model(Person.self)
231 | XCTAssert(person.name == name)
232 |
233 | XCTAssert(person.books?.count == books.count)
234 | XCTAssert(person.books?[0]??.name == books[0].name)
235 | XCTAssert(person.books?[0]??.price == Double(books[0].price))
236 | XCTAssert(person.books?[1]??.name == books[1].name)
237 | XCTAssert(person.books?[1]??.price == Double(books[1].price))
238 |
239 | XCTAssert(person.dogs?.count == dogs.count)
240 | XCTAssert(person.dogs?["dog0"]????.name == dogs[0].name)
241 | XCTAssert(person.dogs?["dog0"]????.age == dogs[0].age)
242 | XCTAssert(person.dogs?["dog1"]????.name == dogs[1].name)
243 | XCTAssert(person.dogs?["dog1"]????.age == dogs[1].age)
244 | }
245 |
246 | func testSet() {
247 | struct Book: Convertible, Hashable {
248 | var name: String = ""
249 | var price: Double = 0.0
250 | }
251 |
252 | struct Person: Convertible {
253 | var name: String = ""
254 | var books: Set?
255 | }
256 |
257 | let name = "Jack"
258 | let bookName = "Fast C++"
259 | let bookPrice = 666.6
260 |
261 | let json: [String: Any] = [
262 | "name": name,
263 | "books": [
264 | ["name": bookName, "price": bookPrice]
265 | ]
266 | ]
267 |
268 | let person = json.kj.model(Person.self)
269 | XCTAssert(person.name == name)
270 |
271 | XCTAssert(person.books?.count == 1)
272 | let book = person.books?.randomElement()
273 | XCTAssert(book?.name == bookName)
274 | XCTAssert(book?.price == bookPrice)
275 | }
276 |
277 | func testModelArrayInDictionary() {
278 | struct Book: Convertible {
279 | var name: String = ""
280 | var price: Double = 0.0
281 | }
282 |
283 | struct Person: Convertible {
284 | var name: String = ""
285 | var books: [String: [Book?]?]?
286 | }
287 |
288 | let name = "Jack"
289 | let mobileBooks = [
290 | (name: "iOS", price: 10.5),
291 | (name: "Android", price: 8.5)
292 | ]
293 | let serverBooks = [
294 | (name: "Java", price: 20.5),
295 | (name: "Go", price: 18.5)
296 | ]
297 |
298 | let json: [String: Any] = [
299 | "name": name,
300 | "books": [
301 | "mobile": [
302 | ["name": mobileBooks[0].name, "price": mobileBooks[0].price],
303 | ["name": mobileBooks[1].name, "price": mobileBooks[1].price]
304 | ],
305 | "server": [
306 | ["name": serverBooks[0].name, "price": serverBooks[0].price],
307 | ["name": serverBooks[1].name, "price": serverBooks[1].price]
308 | ]
309 | ]
310 | ]
311 |
312 | let person = json.kj.model(Person.self)
313 | XCTAssert(person.name == name)
314 | let books0 = person.books?["mobile"]
315 | XCTAssert(books0??.count == mobileBooks.count)
316 | for i in 0.. = [
60 | ["name": tuples[0].name, "price": tuples[0].price]
61 | ]
62 |
63 | let cars = json.kj.modelArray(Car.self)
64 | XCTAssert(cars[0].name == tuples[0].name)
65 | XCTAssert(cars[0].price == tuples[0].price)
66 | }
67 |
68 | func testJSONString() {
69 | let JSONString = """
70 | [
71 | {"name":"\(tuples[0].name)","price":\(tuples[0].price)},
72 | {"name":"\(tuples[1].name)","price":\(tuples[1].price)},
73 | {"name":"\(tuples[2].name)","price":\(tuples[2].price)}
74 | ]
75 | """
76 |
77 | let cars = JSONString.kj.modelArray(Car.self)
78 | check(cars)
79 | }
80 |
81 | func check(_ cars: [Car]) {
82 | XCTAssert(cars.count == tuples.count)
83 | XCTAssert(cars[0].name == tuples[0].name)
84 | XCTAssert(cars[0].price == tuples[0].price)
85 | XCTAssert(cars[1].name == tuples[1].name)
86 | XCTAssert(cars[1].price == tuples[1].price)
87 | XCTAssert(cars[2].name == tuples[2].name)
88 | XCTAssert(cars[2].price == tuples[2].price)
89 | }
90 |
91 | static var allTests = [
92 | "testArray": testArray,
93 | "testNSArray": testNSArray,
94 | "testNSMutableArray": testNSMutableArray,
95 | "testSet": testSet,
96 | "testJSONString": testJSONString
97 | ]
98 | }
99 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/JSON_To_Model/JTM_06_CustomValue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JTM_06_CustomValue.swift
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/8/10.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import KakaJSON
11 |
12 | private let date1Fmt: DateFormatter = {
13 | let fmt = DateFormatter()
14 | fmt.dateFormat = "yyyy-MM-dd"
15 | return fmt
16 | }()
17 |
18 | private let date2Fmt: DateFormatter = {
19 | let fmt = DateFormatter()
20 | fmt.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
21 | return fmt
22 | }()
23 |
24 | class JTM_06_CustomValue: XCTestCase {
25 | // MARK: - Date
26 | func testDate() {
27 | struct Student: Convertible {
28 | var date1: Date?
29 | var date2: NSDate?
30 |
31 | func kj_modelValue(from jsonValue: Any?,
32 | _ property: Property) -> Any? {
33 | switch property.name {
34 | case "date1": return (jsonValue as? String).flatMap(date1Fmt.date)
35 |
36 | // Date to NSDate is a bridging conversion
37 | case "date2": return (jsonValue as? String).flatMap(date2Fmt.date)
38 | default: return jsonValue
39 | }
40 | }
41 | }
42 |
43 | let date1 = "2008-09-09"
44 | let date2 = "2011-11-12 14:20:30.888"
45 |
46 | let json: [String: Any] = [
47 | "date1": date1,
48 | "date2": date2
49 | ]
50 |
51 | let student = json.kj.model(Student.self)
52 | XCTAssert(student.date1.flatMap(date1Fmt.string) == date1)
53 | XCTAssert(student.date2.flatMap(date2Fmt.string) == date2)
54 | }
55 |
56 | // MARK: - Any
57 | func testAny() {
58 | class Dog: Convertible {
59 | var name: String = ""
60 | var weight: Double = 0.0
61 | required init() {}
62 | }
63 |
64 | struct Person: Convertible {
65 | var name: String = ""
66 | // AnyObject、Convertible
67 | var pet: Any?
68 | func kj_modelValue(from jsonValue: Any?,
69 | _ property: Property) -> Any? {
70 | if property.name != "pet" { return jsonValue }
71 | return (jsonValue as? [String: Any])?.kj.model(Dog.self)
72 | }
73 | }
74 |
75 | let name = "Jack"
76 | let dog = (name: "Wang", weight: 109.5)
77 |
78 | let json: [String: Any] = [
79 | "name": name,
80 | "pet": ["name": dog.name, "weight": dog.weight]
81 | ]
82 |
83 | let person = json.kj.model(Person.self)
84 | XCTAssert(person.name == name)
85 |
86 | let pet = person.pet as? Dog
87 | XCTAssert(pet?.name == dog.name)
88 | XCTAssert(pet?.weight == dog.weight)
89 | }
90 |
91 | // MARK: - Array
92 | func testAnyArray() {
93 | class Book: Convertible {
94 | var name: String = ""
95 | var price: Double = 0.0
96 | required init() {}
97 | }
98 |
99 | struct Person: Convertible {
100 | var name: String = ""
101 | // [AnyObject]、[Convertible]、NSArray、NSMutableArray
102 | var books: [Any]?
103 |
104 | func kj_modelValue(from jsonValue: Any?,
105 | _ property: Property) -> Any? {
106 | if property.name != "books" { return jsonValue }
107 | // if books is `NSMutableArray`, neet convert `Array` to `NSMutableArray`
108 | // because `Array` to `NSMutableArray` is not a bridging conversion
109 | return (jsonValue as? [Any])?.kj.modelArray(Book.self)
110 | }
111 | }
112 |
113 | let name = "Jack"
114 | let books = [
115 | (name: "Fast C++", price: 666),
116 | (name: "Data Structure And Algorithm", price: 1666)
117 | ]
118 |
119 | let json: [String: Any] = [
120 | "name": name,
121 | "books": [
122 | ["name": books[0].name, "price": books[0].price],
123 | ["name": books[1].name, "price": books[1].price]
124 | ]
125 | ]
126 |
127 | let person = json.kj.model(Person.self)
128 | XCTAssert(person.name == name)
129 |
130 | XCTAssert(person.books?.count == books.count)
131 |
132 | let book0 = person.books?[0] as? Book
133 | XCTAssert(book0?.name == books[0].name)
134 | XCTAssert(book0?.price == Double(books[0].price))
135 |
136 | let book1 = person.books?[1] as? Book
137 | XCTAssert(book1?.name == books[1].name)
138 | XCTAssert(book1?.price == Double(books[1].price))
139 | }
140 |
141 | // MARK: - NestedArray
142 | func testNestedArray() {
143 | struct Dog: Convertible {
144 | var name: String = ""
145 | var weight: Double = 0.0
146 | }
147 |
148 | struct Person: Convertible {
149 | var name: String = ""
150 | var pet: [[Dog]]?
151 | func kj_modelValue(from jsonValue: Any?,
152 | _ property: Property) -> Any? {
153 | if property.name != "pet" { return jsonValue }
154 | return (jsonValue as? [[[String: Any]]])?.map {
155 | $0.kj.modelArray(Dog.self)
156 | }
157 | }
158 | }
159 |
160 | let name = "Jack"
161 | let dog = (name: "Wang", weight: 109.5)
162 |
163 | let json: [String: Any] = [
164 | "name": name,
165 | "pet": [
166 | [["name": dog.name, "weight": dog.weight]],
167 | [["name": dog.name, "weight": dog.weight]]
168 | ]
169 | ]
170 |
171 | let person = json.kj.model(Person.self)
172 | XCTAssert(person.name == name)
173 |
174 | XCTAssert(person.pet?[0][0].name == dog.name)
175 | XCTAssert(person.pet?[0][0].weight == dog.weight)
176 | XCTAssert(person.pet?[1][0].name == dog.name)
177 | XCTAssert(person.pet?[1][0].weight == dog.weight)
178 | }
179 |
180 | // MARK: - Other
181 | func testOther1() {
182 | struct Student: Convertible {
183 | var age: Int = 0
184 | var name: String = ""
185 |
186 | func kj_modelValue(from jsonValue: Any?,
187 | _ property: Property) -> Any? {
188 | switch property.name {
189 | case "age": return (jsonValue as? Int).flatMap { $0 + 5 }
190 | case "name": return (jsonValue as? String).flatMap { "kj_" + $0 }
191 | default: return jsonValue
192 | }
193 | }
194 | }
195 |
196 | let json: [String: Any] = [
197 | "age": 10,
198 | "name": "Jack"
199 | ]
200 |
201 | let student = json.kj.model(Student.self)
202 | XCTAssert(student.age == 15)
203 | XCTAssert(student.name == "kj_Jack")
204 | }
205 |
206 | func testOther2() {
207 | struct Student: Convertible {
208 | var age: Int = 0
209 | var name: String = ""
210 |
211 | mutating func kj_didConvertToModel(from json: [String: Any]) {
212 | age += 5
213 | name = "kj_" + name
214 | }
215 | }
216 |
217 | let json: [String: Any] = [
218 | "age": 10,
219 | "name": "Jack"
220 | ]
221 |
222 | let student = json.kj.model(Student.self)
223 | XCTAssert(student.age == 15)
224 | XCTAssert(student.name == "kj_Jack")
225 | }
226 |
227 | static var allTests = [
228 | "testDate": testDate,
229 | "testAny": testAny,
230 | "testAnyArray": testAnyArray,
231 | "testNestedArray": testNestedArray,
232 | "testOther1": testOther1,
233 | "testOther2": testOther2
234 | ]
235 | }
236 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/JSON_To_Model/JTM_07_DynamicModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JTM_07_DynamicModel.swift
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/8/12.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import KakaJSON
11 |
12 | class JTM_07_DynamicModel: XCTestCase {
13 | class Book: Convertible {
14 | var name: String = ""
15 | var price: Double = 0.0
16 | required init() {}
17 | }
18 |
19 | class Car: Convertible {
20 | var name: String = ""
21 | var price: Double = 0.0
22 | required init() {}
23 | }
24 |
25 | class Pig: Convertible {
26 | var name: String = ""
27 | var height: Double = 0.0
28 | required init() {}
29 | }
30 |
31 | class Dog: Convertible {
32 | var name: String = ""
33 | var weight: Double = 0.0
34 | required init() {}
35 | }
36 |
37 | struct Person: Convertible {
38 | var name: String = ""
39 | // AnyObject、Convertible
40 | var pet: Any?
41 | // [AnyObject]、[Convertible]、NSArray、NSMutableArray
42 | var toys: [Any]?
43 | // [String: AnyObject]、[String: Convertible]
44 | // NSDictionary、NSMutableDictionary
45 | var foods: [String: Any]?
46 |
47 | func kj_modelType(from jsonValue: Any?,
48 | _ property: Property) -> Convertible.Type? {
49 | switch property.name {
50 | case "pet":
51 | if let pet = jsonValue as? [String: Any],
52 | let _ = pet["height"] {
53 | return Pig.self
54 | }
55 | return Dog.self
56 | case "toys": return Car.self
57 | case "foods": return Book.self
58 | default: return nil
59 | }
60 | }
61 | }
62 |
63 | func test() {
64 | let name = "Jack"
65 | let dog = (name: "Wang", weight: 109.5)
66 | let pig = (name: "Keke", height: 1.55)
67 | let books = [
68 | (name: "Fast C++", price: 666.0),
69 | (name: "Data Structure And Algorithm", price: 1666.0)
70 | ]
71 | let cars = [
72 | (name: "Benz", price: 100.5),
73 | (name: "Bently", price: 300.6)
74 | ]
75 |
76 | let json: [String: Any] = [
77 | "name": name,
78 | "pet": ["name": dog.name, "weight": dog.weight],
79 | // "pet": ["name": pig.name, "height": pig.height],
80 | "toys": [
81 | ["name": cars[0].name, "price": cars[0].price],
82 | ["name": cars[1].name, "price": cars[1].price]
83 | ],
84 | "foods": [
85 | "food0": ["name": books[0].name, "price": books[0].price],
86 | "food1": ["name": books[1].name, "price": books[1].price]
87 | ]
88 | ]
89 |
90 | let person = json.kj.model(Person.self)
91 | XCTAssert(person.name == name)
92 |
93 | if let pet = person.pet as? Dog {
94 | XCTAssert(pet.name == dog.name)
95 | XCTAssert(pet.weight == dog.weight)
96 | } else if let pet = person.pet as? Pig {
97 | XCTAssert(pet.name == pig.name)
98 | XCTAssert(pet.height == pig.height)
99 | }
100 |
101 | let toy0 = person.toys?[0] as? Car
102 | XCTAssert(toy0?.name == cars[0].name)
103 | XCTAssert(toy0?.price == cars[0].price)
104 |
105 | let toy1 = person.toys?[1] as? Car
106 | XCTAssert(toy1?.name == cars[1].name)
107 | XCTAssert(toy1?.price == cars[1].price)
108 |
109 | let food0 = person.foods?["food0"] as? Book
110 | XCTAssert(food0?.name == books[0].name)
111 | XCTAssert(food0?.price == books[0].price)
112 |
113 | let food1 = person.foods?["food1"] as? Book
114 | XCTAssert(food1?.name == books[1].name)
115 | XCTAssert(food1?.price == books[1].price)
116 | }
117 |
118 | static var allTests = [
119 | "test": test
120 | ]
121 | }
122 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/Model_To_JSON/MTJ_01_Basic.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MTJ_01_Basic.swift
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/8/11.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import KakaJSON
11 |
12 | class MTJ_01_Basic: XCTestCase {
13 | // MARK: - Normal
14 | func testNormal() {
15 | // Equatable is only for test cases, is not necessary for Model-To-JSON.
16 | struct Car: Convertible, Equatable {
17 | var name: String = "Bently"
18 | var new: Bool = true
19 | var age: Int = 10
20 | var area: Float = longFloat
21 | var weight: Double = longDouble
22 | var height: Decimal = longDecimal
23 | var price: NSDecimalNumber = longDecimalNumber
24 | var minSpeed: Double = 66.66
25 | var maxSpeed: NSNumber = 77.77
26 | var capacity: CGFloat = 88.88
27 | var birthday: Date = time
28 | var url: URL? = URL(string: "http://520suanfa.com")
29 | }
30 |
31 | let car = Car()
32 |
33 | let json = car.kj.JSONObject()
34 | // let json = JSONObject(from: car)
35 | XCTAssert(Values.bool(json["new"]) == true)
36 | XCTAssert(Values.int(json["age"]) == 10)
37 | XCTAssert(Values.float(json["area"]) == longFloat)
38 | XCTAssert(Values.double(json["weight"]) == longDouble)
39 | XCTAssert(Values.decimal(json["height"]) == longDecimal)
40 | XCTAssert(Values.string(json["name"]) == "Bently")
41 | XCTAssert(Values.decimalNumber(json["price"]) == longDecimalNumber)
42 | XCTAssert(Values.double(json["minSpeed"]) == 66.66)
43 | XCTAssert(Values.number(json["maxSpeed"]) == 77.77)
44 | XCTAssert(Values.cgFloat(json["capacity"]) == 88.88)
45 | XCTAssert(Values.double(json["birthday"]) == timeInteval)
46 | XCTAssert(Values.string(json["url"]) == "http://520suanfa.com")
47 |
48 | var jsonString = car.kj.JSONString()
49 | // var jsonString = JSONString(from: car)
50 | /* {"birthday":1565922866,"new":true,"height":0.123456789012345678901234567890123456789,"weight":0.1234567890123456,"minSpeed":66.66,"price":0.123456789012345678901234567890123456789,"age":10,"name":"Bently","area":0.12345678,"maxSpeed":77.77,"capacity":88.88,"url":"http:\/\/520suanfa.com"} */
51 |
52 | XCTAssert(jsonString.contains("Bently") == true)
53 | XCTAssert(jsonString.contains("true") == true)
54 | XCTAssert(jsonString.contains("10") == true)
55 | XCTAssert(jsonString.contains(longFloatString) == true)
56 | XCTAssert(jsonString.contains(longDoubleString) == true)
57 | XCTAssert(jsonString.contains(longDecimalString) == true)
58 | XCTAssert(jsonString.contains("66.66") == true)
59 | XCTAssert(jsonString.contains("77.77") == true)
60 | XCTAssert(jsonString.contains("88.88") == true)
61 | XCTAssert(jsonString.contains(timeIntevalString) == true)
62 | XCTAssert(jsonString.contains("520suanfa.com") == true)
63 |
64 | jsonString = car.kj.JSONString(prettyPrinted: true)
65 | /*
66 | {
67 | "height" : 0.123456789012345678901234567890123456789,
68 | "weight" : 0.1234567890123456,
69 | "minSpeed" : 66.66,
70 | "new" : true,
71 | "maxSpeed" : 77.77,
72 | "age" : 10,
73 | "capacity" : 88.88,
74 | "birthday" : 1565922866,
75 | "name" : "Bently",
76 | "price" : 0.123456789012345678901234567890123456789,
77 | "area" : 0.12345678
78 | }
79 | */
80 |
81 | // try? jsonString.write(toFile: "/Users/mj/Desktop/car.json", atomically: true, encoding: .utf8)
82 |
83 | checkModelToJSon(Car.self)
84 | }
85 |
86 | // MARK: - Optional Type
87 | func testOptional() {
88 | // Equatable is only for test cases, is not necessary for Model-To-JSON.
89 | struct Student: Convertible, Equatable {
90 | var op1: Int? = 10
91 | var op2: Double?? = 66.66
92 | var op3: Float??? = 77.77
93 | var op4: String???? = "Jack"
94 | var op5: Bool????? = true
95 | // NSArray\Set\NSSet
96 | // NSMutableArray\NSMutableSet
97 | var op6: [CGFloat]?????? = [44.44, 55.55]
98 | }
99 |
100 | let jsonString = Student().kj.JSONString()
101 | /*
102 | {
103 | "op1" : 10,
104 | "op4" : "Jack",
105 | "op2" : 66.66,
106 | "op5" : true,
107 | "op6" : [
108 | 44.44,
109 | 55.55
110 | ],
111 | "op3" : 77.77
112 | }
113 | */
114 |
115 | XCTAssert(jsonString.contains("66.66") == true)
116 | XCTAssert(jsonString.contains("77.77") == true)
117 | XCTAssert(jsonString.contains("true") == true)
118 | XCTAssert(jsonString.contains("44.44") == true)
119 | XCTAssert(jsonString.contains("55.55") == true)
120 |
121 | checkModelToJSon(Student.self)
122 | }
123 |
124 | // MARK: - Enum Type
125 | func testEnum1() {
126 | enum Grade: String, ConvertibleEnum {
127 | case perfect = "A"
128 | case great = "B"
129 | case good = "C"
130 | case bad = "D"
131 | }
132 |
133 | // Equatable is only for test cases, is not necessary for Model-To-JSON.
134 | struct Student: Convertible, Equatable {
135 | var grade1: Grade = .great
136 | var grade2: Grade = .bad
137 | }
138 |
139 | let jsonString = Student().kj.JSONString()
140 | /* {"grade2":"D","grade1":"B"} */
141 |
142 | XCTAssert(jsonString.contains("B") == true)
143 | XCTAssert(jsonString.contains("D") == true)
144 |
145 | checkModelToJSon(Student.self)
146 | }
147 |
148 | func testEnum2() {
149 | enum Grade: Double, ConvertibleEnum {
150 | case perfect = 8.88
151 | case great = 7.77
152 | case good = 6.66
153 | case bad = 5.55
154 | }
155 |
156 | struct Student: Convertible, Equatable {
157 | var grade1: Grade = .perfect
158 | var grade2: Grade = .great
159 | var grade3: Grade = .good
160 | var grade4: Grade = .bad
161 | }
162 |
163 | let jsonString = Student().kj.JSONString()
164 | XCTAssert(jsonString.contains("5.55") == true)
165 | XCTAssert(jsonString.contains("6.66") == true)
166 | XCTAssert(jsonString.contains("7.77") == true)
167 | XCTAssert(jsonString.contains("8.88") == true)
168 |
169 | checkModelToJSon(Student.self)
170 | }
171 |
172 | static var allTests = [
173 | "testNormal": testNormal,
174 | "testOptional": testOptional,
175 | "testEnum1": testEnum1,
176 | "testEnum2": testEnum2
177 | ]
178 | }
179 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/Model_To_JSON/MTJ_02_NestedModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MTJ_02_NestedModel.swift
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/8/12.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import KakaJSON
11 |
12 | class MTJ_02_NestedModel: XCTestCase {
13 | func test() {
14 | // Equatable is only for test cases, is not necessary for Model-To-JSON.
15 | struct Book: Convertible, Equatable {
16 | var name: String = ""
17 | var price: Double = 0.0
18 | }
19 |
20 | // Equatable is only for test cases, is not necessary for Model-To-JSON.
21 | struct Car: Convertible, Equatable {
22 | var name: String = ""
23 | var price: Double = 0.0
24 | }
25 |
26 | // Equatable is only for test cases, is not necessary for Model-To-JSON.
27 | struct Dog: Convertible, Equatable {
28 | var name: String = ""
29 | var age: Int = 0
30 | }
31 |
32 | // Equatable is only for test cases, is not necessary for Model-To-JSON.
33 | struct Person: Convertible, Equatable {
34 | var name: String = "Jack"
35 | var car: Car? = Car(name: "Bently", price: 106.666)
36 | var books: [Book]? = [
37 | Book(name: "Fast C++", price: 666.6),
38 | Book(name: "Data Structure And Algorithm", price: 666.6),
39 | ]
40 | var dogs: [String: Dog]? = [
41 | "dog0": Dog(name: "Wang", age: 5),
42 | "dog1": Dog(name: "ErHa", age: 3),
43 | ]
44 | }
45 |
46 | let jsonString = Person().kj.JSONString()
47 | /*
48 | {
49 | "dogs" : {
50 | "dog0" : {
51 | "name" : "Wang",
52 | "age" : 5
53 | },
54 | "dog1" : {
55 | "name" : "ErHa",
56 | "age" : 3
57 | }
58 | },
59 | "books" : [
60 | {
61 | "price" : 666.6,
62 | "name" : "Fast C++"
63 | },
64 | {
65 | "name" : "Data Structure And Algorithm",
66 | "price" : 666.6
67 | }
68 | ],
69 | "name" : "Jack",
70 | "car" : {
71 | "price" : 106.666,
72 | "name" : "Bently"
73 | }
74 | }
75 | */
76 |
77 | XCTAssert(jsonString.contains("106.666") == true)
78 | XCTAssert(jsonString.contains("666.6") == true)
79 | XCTAssert(jsonString.contains("Data Structure And Algorithm") == true)
80 |
81 | checkModelToJSon(Person.self)
82 | }
83 |
84 | func testAny() {
85 | struct Book: Convertible {
86 | var name: String = ""
87 | var price: Double = 0.0
88 | }
89 |
90 | struct Dog: Convertible {
91 | var name: String = ""
92 | var age: Int = 0
93 | }
94 |
95 | struct Person: Convertible {
96 | // NSArray\NSMutableArray
97 | var books: [Any]? = [
98 | Book(name: "Fast C++", price: 666.6),
99 | Book(name: "Data Structure And Algorithm", price: 1666.6),
100 | ]
101 |
102 | // NSDictionary\NSMutableDictionary
103 | var dogs: [String: Any]? = [
104 | "dog0": Dog(name: "Wang", age: 5),
105 | "dog1": Dog(name: "ErHa", age: 3),
106 | ]
107 | }
108 |
109 | let jsonString = Person().kj.JSONString()
110 | /*
111 | {
112 | "dogs" : {
113 | "dog1" : {
114 | "age" : 3,
115 | "name" : "ErHa"
116 | },
117 | "dog0" : {
118 | "age" : 5,
119 | "name" : "Wang"
120 | }
121 | },
122 | "books" : [
123 | {
124 | "name" : "Fast C++",
125 | "price" : 666.6
126 | },
127 | {
128 | "price" : 1666.6,
129 | "name" : "Data Structure And Algorithm"
130 | }
131 | ]
132 | }
133 | */
134 | XCTAssert(jsonString.contains("1666.6") == true)
135 | XCTAssert(jsonString.contains("666.6") == true)
136 | XCTAssert(jsonString.contains("Fast C++") == true)
137 | }
138 |
139 | static var allTests = [
140 | "test": test,
141 | "testAny": testAny
142 | ]
143 | }
144 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/Model_To_JSON/MTJ_03_ModelArray.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MTJ_03_ModelArray.swift
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/8/12.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import KakaJSON
11 |
12 | class MTJ_03_ModelArray: XCTestCase {
13 |
14 | func testArray() {
15 | struct Car: Convertible {
16 | var name: String = ""
17 | var price: Double = 0.0
18 | }
19 |
20 | // NSArray\NSMutableArray
21 | let models = [
22 | Car(name: "BMW", price: 100.0),
23 | Car(name: "Audi", price: 70.0),
24 | Car(name: "Bently", price: 300.0)
25 | ]
26 |
27 | let jsonString = models.kj.JSONString()
28 | /*
29 | [
30 | {
31 | "name" : "BMW",
32 | "price" : 100
33 | },
34 | {
35 | "price" : 70,
36 | "name" : "Audi"
37 | },
38 | {
39 | "price" : 300,
40 | "name" : "Bently"
41 | }
42 | ]
43 | */
44 | XCTAssert(jsonString.contains("BMW") == true)
45 | XCTAssert(jsonString.contains("100") == true)
46 | XCTAssert(jsonString.contains("Audi") == true)
47 | XCTAssert(jsonString.contains("70") == true)
48 | XCTAssert(jsonString.contains("Bently") == true)
49 | XCTAssert(jsonString.contains("300") == true)
50 | }
51 |
52 | func testSet() {
53 | struct Car: Convertible, Hashable {
54 | var name: String = ""
55 | var price: Double = 0.0
56 | }
57 |
58 | let models: Set = [
59 | Car(name: "BMW", price: 100.0),
60 | Car(name: "Audi", price: 70.0),
61 | Car(name: "Bently", price: 300.0)
62 | ]
63 |
64 | let jsonString = models.kj.JSONString()
65 | /*
66 | [
67 | {
68 | "price" : 70,
69 | "name" : "Audi"
70 | },
71 | {
72 | "price" : 300,
73 | "name" : "Bently"
74 | },
75 | {
76 | "name" : "BMW",
77 | "price" : 100
78 | }
79 | ]
80 | */
81 | XCTAssert(jsonString.contains("BMW") == true)
82 | XCTAssert(jsonString.contains("100") == true)
83 | XCTAssert(jsonString.contains("Audi") == true)
84 | XCTAssert(jsonString.contains("70") == true)
85 | XCTAssert(jsonString.contains("Bently") == true)
86 | XCTAssert(jsonString.contains("300") == true)
87 | }
88 |
89 | static var allTests = [
90 | "testArray": testArray,
91 | "testSet": testSet
92 | ]
93 | }
94 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/Model_To_JSON/MTJ_04_KeyMapping.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MTJ_04_KeyMapping.swift
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/8/13.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import KakaJSON
11 |
12 | class MTJ_04_KeyMapping: XCTestCase {
13 | struct Dog: Convertible {
14 | var nickName: String = "Wang"
15 | var price: Double = 100.6
16 |
17 | func kj_JSONKey(from property: Property) -> JSONPropertyKey {
18 | switch property.name {
19 | case "nickName": return "_nick_name_"
20 | default: return property.name
21 | }
22 | }
23 | }
24 |
25 | func test() {
26 | let jsonString = Dog().kj.JSONString()
27 | /* {"price":100.6,"_nick_name_":"Wang"} */
28 | XCTAssert(jsonString.contains("_nick_name_") == true)
29 | }
30 |
31 | static var allTests = [
32 | "test": test
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/Model_To_JSON/MTJ_05_CustomValue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MTJ_05_CustomValue.swift
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/8/13.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import KakaJSON
11 |
12 | private let dateFmt: DateFormatter = {
13 | let fmt = DateFormatter()
14 | fmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
15 | return fmt
16 | }()
17 |
18 | class MTJ_05_CustomValue: XCTestCase {
19 | // MARK: - Date
20 | func testDate() {
21 | struct Student: Convertible {
22 | var birthday: Date?
23 |
24 | func kj_JSONValue(from modelValue: Any?,
25 | _ property: Property) -> Any? {
26 | if property.name != "birthday" { return modelValue }
27 | return birthday.flatMap(dateFmt.string)
28 | }
29 | }
30 |
31 | let time = "2019-08-13 12:52:51"
32 | let date = dateFmt.date(from: time)
33 | let student = Student(birthday: date)
34 | let jsonString = student.kj.JSONString()
35 | /* {"birthday":"2019-08-13 12:52:51"} */
36 | XCTAssert(jsonString.contains(time) == true)
37 | }
38 |
39 | static var allTests = [
40 | "testDate": testDate
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/OC/KakaJSONTests-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "OCModel.h"
6 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/OC/OCModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // OCModel.h
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/12/23.
6 | //
7 |
8 | #import
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @interface OCModel : NSObject
13 | @property (nonatomic, assign) int no;
14 | @end
15 |
16 | NS_ASSUME_NONNULL_END
17 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/OC/OCModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // OCModel.m
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/12/23.
6 | //
7 |
8 | #import "OCModel.h"
9 |
10 | @implementation OCModel
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/Other/Bugs.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Bugs.swift
3 | // KakaJSON
4 | //
5 | // Created by MJ Lee on 2020/1/7.
6 | //
7 |
8 | import XCTest
9 | @testable import KakaJSON
10 |
11 | class TestBugs: XCTestCase {
12 | // https://github.com/kakaopensource/KakaJSON/issues/39
13 | func testIssue39() {
14 | struct ChatRoomModel: Convertible {
15 | var lastMessage: String = ""
16 | var lastMessageTime: NSNumber = 0
17 | var pkid: NSNumber = 0
18 | var unreadNoticeCount: NSNumber = 0
19 | }
20 | struct WorkShopsModel: Convertible {
21 | var chatRoom: ChatRoomModel?
22 | var imgUrl: String = ""
23 | var isMine: NSNumber = false
24 | var pkid: NSNumber = 0
25 | var unsignined: NSNumber?
26 | var status: NSNumber = 0
27 | var title: String = ""
28 | var regCount: Int?
29 | var tbString: String?
30 | var teString: String?
31 | }
32 |
33 | let json: [String: Any] = [
34 | "chatRoom": [
35 | "last_message": "666",
36 | "last_message_time": 1567578912000,
37 | "pkid": 2,
38 | "unread_notice_count": 10
39 | ],
40 | "img_url": "xx.jpg",
41 | "is_mine": true,
42 | "pkid": 3,
43 | "reg_count": 1,
44 | "status": 1,
45 | "tbString": "2019-01-30",
46 | "teString": "2019-09-28",
47 | "title": "777",
48 | "unsignined": true
49 | ]
50 |
51 | ConvertibleConfig.setModelKey { (property) in
52 | return property.name.kj.underlineCased()
53 | // return [property.name.kj.underlineCased()]
54 | }
55 |
56 | let workShops = json.kj.model(WorkShopsModel.self)
57 | XCTAssert(workShops.chatRoom?.lastMessage == "666")
58 | XCTAssert(workShops.chatRoom?.pkid == 2)
59 | XCTAssert(workShops.chatRoom?.unreadNoticeCount == 10)
60 | XCTAssert(workShops.chatRoom?.lastMessageTime == 1567578912000)
61 | XCTAssert(workShops.imgUrl == "xx.jpg")
62 | XCTAssert(workShops.isMine == true)
63 | XCTAssert(workShops.pkid == 3)
64 | XCTAssert(workShops.regCount == 1)
65 | XCTAssert(workShops.status == 1)
66 | XCTAssert(workShops.tbString == "2019-01-30")
67 | XCTAssert(workShops.teString == "2019-09-28")
68 | XCTAssert(workShops.title == "777")
69 | XCTAssert(workShops.unsignined == true)
70 | }
71 |
72 | static var allTests = [
73 | "testIssue39": testIssue39
74 | ]
75 | }
76 |
77 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/Other/Coding.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Coding.swift
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/8/22.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import KakaJSON
11 |
12 | class TestCoding: XCTestCase {
13 | // Please input your file path
14 | var file: String = {
15 | var str = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0]
16 | str.append("/kj_test.json")
17 | return str
18 | }()
19 |
20 | func testModel() {
21 | // Equatable is only for test cases, is not necessary for Coding.
22 | struct Car: Convertible, Equatable {
23 | var name: String = "Bently"
24 | var new: Bool = true
25 | var age: Int = 10
26 | var area: Float = longFloat
27 | var weight: Double = longDouble
28 | var height: Decimal = longDecimal
29 | var price: NSDecimalNumber = longDecimalNumber
30 | var minSpeed: Double = 66.66
31 | var maxSpeed: NSNumber = 77.77
32 | var capacity: CGFloat = 88.88
33 | var birthday: Date = time
34 | var url: URL? = URL(string: "http://520suanfa.com")
35 | }
36 |
37 | checkCoding(Car.self)
38 | }
39 |
40 | func testOptional() {
41 | // Equatable is only for test cases, is not necessary for Coding.
42 | struct Student: Convertible, Equatable {
43 | var op1: Int? = 10
44 | var op2: Double?? = 66.66
45 | var op3: Float??? = 77.77
46 | var op4: String???? = "Jack"
47 | var op5: Bool????? = true
48 | // NSArray\Set\NSSet
49 | // NSMutableArray\NSMutableSet
50 | var op6: [CGFloat]?????? = [44.44, 55.55]
51 | }
52 |
53 | checkCoding(Student.self)
54 | }
55 |
56 | func testNested() {
57 | // Equatable is only for test cases, is not necessary for Coding.
58 | struct Book: Convertible, Equatable {
59 | var name: String = ""
60 | var price: Double = 0.0
61 | }
62 |
63 | // Equatable is only for test cases, is not necessary for Coding.
64 | struct Car: Convertible, Equatable {
65 | var name: String = ""
66 | var price: Double = 0.0
67 | }
68 |
69 | // Equatable is only for test cases, is not necessary for Coding.
70 | struct Dog: Convertible, Equatable {
71 | var name: String = ""
72 | var age: Int = 0
73 | }
74 |
75 | // Equatable is only for test cases, is not necessary for Coding.
76 | struct Person: Convertible, Equatable {
77 | var name: String = "Jack"
78 | var car: Car? = Car(name: "Bently", price: 106.666)
79 | var books: [Book]? = [
80 | Book(name: "Fast C++", price: 666.6),
81 | Book(name: "Data Structure And Algorithm", price: 666.6),
82 | ]
83 | var dogs: [String: Dog]? = [
84 | "dog0": Dog(name: "Wang", age: 5),
85 | "dog1": Dog(name: "ErHa", age: 3),
86 | ]
87 | }
88 |
89 | checkCoding(Person.self)
90 |
91 | write(Person(), to: file)
92 | let person = read(Person.self, from: file)
93 |
94 | XCTAssert(person?.name == "Jack")
95 | XCTAssert(person?.car?.name == "Bently")
96 | XCTAssert(person?.car?.price == 106.666)
97 | XCTAssert(person?.books?.count == 2)
98 | XCTAssert(person?.dogs?.count == 2)
99 | }
100 |
101 | func testAny() {
102 | struct Book: Convertible {
103 | var name: String = ""
104 | var price: Double = 0.0
105 | }
106 |
107 | struct Dog: Convertible {
108 | var name: String = ""
109 | var age: Int = 0
110 | }
111 |
112 | struct Person: Convertible {
113 | // NSArray\NSMutableArray
114 | var books: [Any]? = [
115 | Book(name: "Fast C++", price: 666.6),
116 | Book(name: "Data Structure And Algorithm", price: 1666.6),
117 | ]
118 |
119 | // NSDictionary\NSMutableDictionary
120 | var dogs: [String: Any]? = [
121 | "dog0": Dog(name: "Wang", age: 5),
122 | "dog1": Dog(name: "ErHa", age: 3),
123 | ]
124 | }
125 |
126 | write(Person(), to: file)
127 | let person = read(Person.self, from: file)
128 | XCTAssert(person?.books?.count == 2)
129 | XCTAssert(person?.dogs?.count == 2)
130 | let personString = "\(person as Any)"
131 | XCTAssert(personString.contains("Fast C++"))
132 | XCTAssert(personString.contains("Data Structure And Algorithm"))
133 | XCTAssert(personString.contains("666.6"))
134 | XCTAssert(personString.contains("1666.6"))
135 | // prevent from 66.6499999999999998
136 | XCTAssert(!personString.contains("99999"))
137 | // prevent from 66.6600000000000001
138 | XCTAssert(!personString.contains("00000"))
139 | }
140 |
141 | func testDate() {
142 | let date1 = Date(timeIntervalSince1970: timeInteval)
143 | write(date1, to: file)
144 |
145 | let date2 = read(Date.self, from: file)
146 | XCTAssert(date2 == date1)
147 |
148 | XCTAssert(read(Double.self, from: file) == timeInteval)
149 | }
150 |
151 | func testString() {
152 | let string1 = "123"
153 | write(string1, to: file)
154 |
155 | let string2 = read(String.self, from: file)
156 | XCTAssert(string2 == string1)
157 |
158 | XCTAssert(read(Int.self, from: file) == 123)
159 | }
160 |
161 | func testArray() {
162 | let array1 = ["Jack", "Rose"]
163 | write(array1, to: file)
164 |
165 | let array2 = read([String].self, from: file)
166 | XCTAssert(array2 == array1)
167 | }
168 |
169 | func testModelArray() {
170 | struct Car: Convertible {
171 | var name: String = ""
172 | var price: Double = 0.0
173 | }
174 |
175 | let models1 = [
176 | Car(name: "BMW", price: 100.0),
177 | Car(name: "Audi", price: 70.0)
178 | ]
179 |
180 | write(models1, to: file)
181 |
182 | let models2 = read([Car].self, from: file)
183 | XCTAssert(models2?.count == models1.count)
184 | XCTAssert(models2?[0].name == "BMW")
185 | XCTAssert(models2?[0].price == 100.0)
186 | XCTAssert(models2?[1].name == "Audi")
187 | XCTAssert(models2?[1].price == 70.0)
188 | }
189 |
190 | func testModelSet() {
191 | struct Car: Convertible, Hashable {
192 | var name: String = ""
193 | var price: Double = 0.0
194 | }
195 |
196 | let models1: Set = [
197 | Car(name: "BMW", price: 100.0),
198 | Car(name: "Audi", price: 70.0)
199 | ]
200 |
201 | write(models1, to: file)
202 |
203 | let models2 = read(Set.self, from: file)!
204 |
205 | XCTAssert(models2.count == models1.count)
206 | for car in models2 {
207 | XCTAssert(["BMW", "Audi"].contains(car.name))
208 | XCTAssert([100.0, 70.0].contains(car.price))
209 | }
210 | }
211 |
212 | func testModelDictionary() {
213 | struct Car: Convertible {
214 | var name: String = ""
215 | var price: Double = 0.0
216 | }
217 |
218 | let models1 = [
219 | "car0": Car(name: "BMW", price: 100.0),
220 | "car1": Car(name: "Audi", price: 70.0)
221 | ]
222 |
223 | write(models1, to: file)
224 |
225 | let models2 = read([String: Car].self, from: file)
226 | XCTAssert(models2?.count == models1.count)
227 |
228 | let car0 = models2?["car0"]
229 | XCTAssert(car0?.name == "BMW")
230 | XCTAssert(car0?.price == 100.0)
231 |
232 | let car1 = models2?["car1"]
233 | XCTAssert(car1?.name == "Audi")
234 | XCTAssert(car1?.price == 70.0)
235 | }
236 |
237 | func checkCoding(_ type: M.Type) {
238 | let obj1 = type.init()
239 | // write to file
240 | write(obj1, to: file)
241 |
242 | // read from file
243 | let obj2 = read(type, from: file)
244 | XCTAssert(obj1 == obj2)
245 |
246 | let objString = "\(obj2 as Any)"
247 | // prevent from 66.6499999999999998
248 | XCTAssert(!objString.contains("99999"))
249 | // prevent from 66.6600000000000001
250 | XCTAssert(!objString.contains("00000"))
251 | }
252 |
253 | static var allTests = [
254 | "testModel": testModel,
255 | "testOptional": testOptional,
256 | "testNested": testNested,
257 | "testAny": testAny,
258 | "testDate": testDate,
259 | "testString": testString,
260 | "testArray": testArray,
261 | "testModelArray": testModelArray,
262 | "testModelSet": testModelSet,
263 | "testModelDictionary": testModelDictionary
264 | ]
265 | }
266 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/Other/Global.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Global.swift
3 | // KakaJSONTests
4 | //
5 | // Created by MJ Lee on 2019/8/11.
6 | // Copyright © 2019 MJ Lee. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import KakaJSON
11 |
12 | class TestGlobal: XCTestCase {
13 | struct Cat: Convertible {
14 | var age: Int = 0
15 | var name: String = ""
16 | }
17 |
18 | func testMetadata() {
19 | struct Cat {
20 | var age: Int = 0
21 | let name: String = ""
22 | }
23 |
24 | guard let type = Metadata.type(Cat.self) as? ModelType,
25 | let properties = type.properties else { return }
26 |
27 | for property in properties {
28 | print(property.name,
29 | property.type,
30 | property.isVar,
31 | property.ownerType,
32 | property.offset)
33 | // age Int true Cat 0
34 | // name String false Cat 8
35 | }
36 | }
37 |
38 | func testValue() {
39 | let string1 = "1565922866"
40 | XCTAssert(Values.int(string1) == 1565922866)
41 | XCTAssert(Values.double(string1) == 1565922866)
42 | XCTAssert(Values.cgFloat(string1) == 1565922866)
43 | let date = Values.value(string1, of: Date.self)
44 | XCTAssert(date?.timeIntervalSince1970 == 1565922866)
45 |
46 | let string2 = "true"
47 | XCTAssert(Values.bool(string2) == true)
48 | XCTAssert(Values.int(string2) == 1)
49 | }
50 |
51 | func testJSON_To_Model() {
52 | let name = "Miaomiao"
53 | let age = 26
54 | let json: [String: Any] = [
55 | "age": age,
56 | "name": name
57 | ]
58 | let jsonString = """
59 | {
60 | "name": "\(name)",
61 | "age": \(age)
62 | }
63 | """
64 | let jsonData = jsonString.data(using: .utf8)!
65 |
66 | let cat1 = model(from: json, Cat.self)
67 | XCTAssert(cat1.name == name)
68 | XCTAssert(cat1.age == age)
69 |
70 | let cat2 = model(from: json, type: Cat.self) as? Cat
71 | XCTAssert(cat2?.name == name)
72 | XCTAssert(cat2?.age == age)
73 |
74 | let cat3 = model(from: jsonString, Cat.self)
75 | XCTAssert(cat3?.name == name)
76 | XCTAssert(cat3?.age == age)
77 |
78 | let cat4 = model(from: jsonString, type: Cat.self) as? Cat
79 | XCTAssert(cat4?.name == name)
80 | XCTAssert(cat4?.age == age)
81 |
82 | let cat5 = model(from: jsonData, Cat.self)
83 | XCTAssert(cat5?.name == name)
84 | XCTAssert(cat5?.age == age)
85 |
86 | let cat6 = model(from: jsonData, type: Cat.self) as? Cat
87 | XCTAssert(cat6?.name == name)
88 | XCTAssert(cat6?.age == age)
89 | }
90 |
91 | func testModel_To_JSON() {
92 | let cat = Cat(age: 26, name: "Miaomiao")
93 |
94 | let json = JSONObject(from: cat)
95 | XCTAssert(json["name"] as? String == cat.name)
96 | XCTAssert(json["age"] as? Int == cat.age)
97 |
98 | let jsonString = JSONString(from: cat)
99 | XCTAssert(jsonString.contains("\"age\":\(cat.age)") == true)
100 | XCTAssert(jsonString.contains("\"name\":\"\(cat.name)\"") == true)
101 | }
102 |
103 | func testJSONArray_To_ModelArray() {
104 | let name = "Miaomiao"
105 | let age = 26
106 | let json: [[String: Any]] = [
107 | ["age": age, "name": name],
108 | ["age": age, "name": name]
109 | ]
110 | let jsonString = """
111 | [
112 | {"name": "\(name)", "age": \(age)},
113 | {"name": "\(name)", "age": \(age)}
114 | ]
115 | """
116 |
117 | let cats1 = modelArray(from: json, Cat.self)
118 | XCTAssert(cats1[0].name == name)
119 | XCTAssert(cats1[0].age == age)
120 | XCTAssert(cats1[1].name == name)
121 | XCTAssert(cats1[1].age == age)
122 |
123 | let cats2 = modelArray(from: json, type: Cat.self) as? [Cat]
124 | XCTAssert(cats2?[0].name == name)
125 | XCTAssert(cats2?[0].age == age)
126 | XCTAssert(cats2?[1].name == name)
127 | XCTAssert(cats2?[1].age == age)
128 |
129 | let cats3 = modelArray(from: jsonString, Cat.self)
130 | XCTAssert(cats3[0].name == name)
131 | XCTAssert(cats3[0].age == age)
132 | XCTAssert(cats3[1].name == name)
133 | XCTAssert(cats3[1].age == age)
134 |
135 | let cats4 = modelArray(from: jsonString, type: Cat.self) as? [Cat]
136 | XCTAssert(cats4?[0].name == name)
137 | XCTAssert(cats4?[0].age == age)
138 | XCTAssert(cats4?[1].name == name)
139 | XCTAssert(cats4?[1].age == age)
140 | }
141 |
142 | func testModelArray_To_JSONArray() {
143 | let cat = Cat(age: 26, name: "Miaomiao")
144 | let cats = [cat, cat]
145 |
146 | let json = JSONObjectArray(from: cats)
147 | XCTAssert(json[0]["name"] as? String == cat.name)
148 | XCTAssert(json[0]["age"] as? Int == cat.age)
149 | XCTAssert(json[1]["name"] as? String == cat.name)
150 | XCTAssert(json[1]["age"] as? Int == cat.age)
151 |
152 | let jsonString = JSONString(from: cats)
153 | XCTAssert(jsonString.contains("[") == true)
154 | XCTAssert(jsonString.contains("},{") == true)
155 | XCTAssert(jsonString.contains("]") == true)
156 | XCTAssert(jsonString.contains("\"age\":\(cat.age)") == true)
157 | XCTAssert(jsonString.contains("\"name\":\"\(cat.name)\"") == true)
158 | }
159 |
160 | static var allTests = [
161 | "testMetadata": testMetadata,
162 | "testValue": testValue,
163 | "testJSON_To_Model": testJSON_To_Model,
164 | "testModel_To_JSON": testModel_To_JSON,
165 | "testJSONArray_To_ModelArray": testJSONArray_To_ModelArray,
166 | "testModelArray_To_JSONArray": testModelArray_To_JSONArray
167 | ]
168 | }
169 |
170 | let timeIntevalInt: Int = 1565922866
171 | let timeIntevalFloat = Float(timeIntevalInt)
172 | let timeInteval = Double(timeIntevalInt)
173 | let timeIntevalString = "\(timeIntevalInt)"
174 | let time = Date(timeIntervalSince1970: timeInteval)
175 | // 16 decimals
176 | let longDoubleString = "0.1234567890123456"
177 | let longDouble: Double = 0.1234567890123456
178 | // 8 decimals
179 | let longFloatString = "0.12345678"
180 | let longFloat: Float = 0.12345678
181 | // 39 decimals
182 | let longDecimalString = "0.123456789012345678901234567890123456789"
183 | var longDecimal = Decimal(string: longDecimalString)!
184 | var longDecimalNumber = NSDecimalNumber(string: longDecimalString)
185 |
186 | func checkModelToJSon(_ type: M.Type) {
187 | // create model
188 | let model = type.init()
189 | // get JSONString from model
190 | let jsonString = model.kj.JSONString()
191 |
192 | // check JSON and JSONString
193 | let modelFromJsonString = jsonString.kj.model(type: type) as? M
194 | XCTAssert(model == modelFromJsonString)
195 |
196 | // prevent from 66.6499999999999998
197 | XCTAssert(jsonString.contains("99999") == false)
198 | // prevent from 66.6600000000000001
199 | XCTAssert(jsonString.contains("00000") == false)
200 | }
201 |
202 | /// note
203 | /*
204 | protocol Runnable {}
205 | class Dog: Runnable {}
206 |
207 | print("--------section0--------")
208 | print(Runnable.self is Runnable.Type) // false
209 |
210 | print("--------section1--------")
211 | let dog1: Dog = Dog()
212 | print(dog1 as? Runnable) // success
213 | print(dog1 is Runnable) // true
214 | print(dog1 as? Dog) // success
215 | print(dog1 is Dog) // true
216 |
217 | print("--------section2--------")
218 | let dog2 = Dog.self
219 | print(dog2 as? Runnable.Type) // success
220 | print(dog2 is Runnable.Type) // true
221 | print(dog2 as? Dog.Type) // success
222 | print(dog2 is Dog.Type) // true
223 |
224 | print("--------section3--------")
225 | let dog3: dog?? = Dog()
226 | print(dog3 as? Runnable) // success
227 | print(dog3 is Runnable) // true
228 | print(dog3 as? Dog) // success
229 | print(dog3 is Dog) // true
230 |
231 | print("--------section4--------")
232 | let dog4: dog?? = nil
233 | print(dog4 as? Runnable) // nil
234 | print(dog4 is Runnable) // false
235 | print(dog4 as? Dog) // nil
236 | print(dog4 is Dog) // false
237 |
238 | print("--------section5--------")
239 | let dog5 = dog??.self
240 | print(dog5 as? Runnable.Type) // nil
241 | print(dog5 is Runnable.Type) // false
242 | print(dog5 as? Dog.Type) // nil
243 | print(dog5 is Dog.Type) // false
244 |
245 | print("--------section6--------")
246 | let dog6: Dog = Dog()
247 | print(dog6 as? Runnable???) // success
248 | print(dog6 is Runnable???) // false
249 | print(dog6 as? dog??) // success
250 | print(dog6 is dog??) // false
251 |
252 | print("--------section7--------")
253 | let dog7 = Dog.self
254 | print(dog7 as? Runnable???.Type) // nil
255 | print(dog7 is Runnable???.Type) // false
256 | print(dog7 as? dog??.Type) // nil
257 | print(dog7 is dog??.Type) // false
258 | */
259 |
260 | /*
261 | class Runnable {}
262 | class Dog: Runnable {}
263 |
264 | print("--------section0--------")
265 | print(Runnable.self is Runnable.Type) // true
266 |
267 | print("--------section1--------")
268 | let dog1: Dog = Dog()
269 | print(dog1 as? Runnable) // success
270 | print(dog1 is Runnable) // true
271 | print(dog1 as? Dog) // success
272 | print(dog1 is Dog) // true
273 |
274 | print("--------section2--------")
275 | let dog2 = Dog.self
276 | print(dog2 as? Runnable.Type) // success
277 | print(dog2 is Runnable.Type) // true
278 | print(dog2 as? Dog.Type) // success
279 | print(dog2 is Dog.Type) // true
280 |
281 | print("--------section3--------")
282 | let dog3: dog?? = Dog()
283 | print(dog3 as? Runnable) // success
284 | print(dog3 is Runnable) // true
285 | print(dog3 as? Dog) // success
286 | print(dog3 is Dog) // true
287 |
288 | print("--------section4--------")
289 | let dog4: dog?? = nil
290 | print(dog4 as? Runnable) // nil
291 | print(dog4 is Runnable) // false
292 | print(dog4 as? Dog) // nil
293 | print(dog4 is Dog) // false
294 |
295 | print("--------section5--------")
296 | let dog5 = dog??.self
297 | print(dog5 as? Runnable.Type) // nil
298 | print(dog5 is Runnable.Type) // false
299 | print(dog5 as? Dog.Type) // nil
300 | print(dog5 is Dog.Type) // false
301 |
302 | print("--------section6--------")
303 | let dog6: Dog = Dog()
304 | print(dog6 as? Runnable???) // success
305 | print(dog6 is Runnable???) // false
306 | print(dog6 as? dog??) // success
307 | print(dog6 is dog??) // false
308 |
309 | print("--------section7--------")
310 | let dog7 = Dog.self
311 | print(dog7 as? Runnable???.Type) // nil
312 | print(dog7 is Runnable???.Type) // false
313 | print(dog7 as? dog??.Type) // nil
314 | print(dog7 is dog??.Type) // false
315 | */
316 |
317 | /*
318 | let array: [Int?] = [1, nil, 2]
319 | print(array as? [Any]) // success
320 | print(array as? [Int]) // nil
321 | */
322 |
--------------------------------------------------------------------------------
/Tests/KakaJSONTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(JTM_01_Basic.allTests),
7 | testCase(JTM_02_DataType.allTests),
8 | testCase(JTM_03_NestedModel.allTests),
9 | testCase(JTM_04_ModelArray.allTests),
10 | testCase(JTM_05_KeyMapping.allTests),
11 | testCase(JTM_06_CustomValue.allTests),
12 | testCase(JTM_07_DynamicModel.allTests),
13 | testCase(MTJ_01_Basic.allTests),
14 | testCase(MTJ_02_NestedModel.allTests),
15 | testCase(MTJ_03_ModelArray.allTests),
16 | testCase(MTJ_04_KeyMapping.allTests),
17 | testCase(MTJ_05_CustomValue.allTests),
18 | testCase(TestCoding.allTests),
19 | testCase(TestGlobal.allTests),
20 | testCase(TestBugs.allTests)
21 | ]
22 | }
23 | #endif
24 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import KakaJSONTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += KakaJSONTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------