├── .gitignore ├── Demo ├── Pods │ ├── Target Support Files │ │ ├── SCJSONUtil │ │ │ ├── SCJSONUtil.modulemap │ │ │ ├── SCJSONUtil-dummy.m │ │ │ ├── SCJSONUtil-prefix.pch │ │ │ ├── SCJSONUtil-umbrella.h │ │ │ ├── SCJSONUtil.xcconfig │ │ │ └── SCJSONUtil-Info.plist │ │ └── Pods-SCJSONUtilDemo │ │ │ ├── Pods-SCJSONUtilDemo.modulemap │ │ │ ├── Pods-SCJSONUtilDemo-dummy.m │ │ │ ├── Pods-SCJSONUtilDemo-umbrella.h │ │ │ ├── Pods-SCJSONUtilDemo-Info.plist │ │ │ ├── Pods-SCJSONUtilDemo-acknowledgements.markdown │ │ │ └── Pods-SCJSONUtilDemo-acknowledgements.plist │ ├── Manifest.lock │ └── Pods.xcodeproj │ │ └── xcuserdata │ │ └── qianlongxu.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── SCJSONUtilDemo │ ├── jsonfiles │ │ ├── VideoInfo.json │ │ ├── Store.json │ │ ├── Car.json │ │ ├── OCTypes.json │ │ ├── FavConcern.json │ │ ├── Userinfo.json │ │ ├── DynamicVideos.json │ │ ├── GalleryList.json │ │ └── NewRecProduct.json │ ├── models │ │ ├── Car.m │ │ ├── FavModel.m │ │ ├── GalleryModel.m │ │ ├── OCTypes.m │ │ ├── StoreModel.m │ │ ├── FavModel.h │ │ ├── StoreModel.h │ │ ├── VideoInfo.h │ │ ├── GalleryModel.h │ │ ├── DynamicVideos.m │ │ ├── UserInfoModel.m │ │ ├── DynamicVideos.h │ │ ├── UserInfoModel.h │ │ ├── OCTypes.h │ │ ├── VideoInfo.m │ │ └── Car.h │ ├── ViewController.h │ ├── AppDelegate.h │ ├── main.m │ ├── Test.h │ ├── util │ │ ├── Util.h │ │ └── Util.m │ ├── ViewController.m │ ├── Info.plist │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── AppDelegate.m │ └── Test.m ├── SCJSONUtilDemo.xcodeproj │ ├── xcuserdata │ │ ├── qianlongxu.xcuserdatad │ │ │ ├── xcdebugger │ │ │ │ └── Breakpoints_v2.xcbkptlist │ │ │ └── xcschemes │ │ │ │ └── xcschememanagement.plist │ │ ├── matt.xcuserdatad │ │ │ └── xcschemes │ │ │ │ └── xcschememanagement.plist │ │ └── crown.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ └── SCJSONUtilDemo.xcscheme │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcuserdata │ │ ├── crown.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ │ └── qianlongxu.xcuserdatad │ │ │ ├── UserInterfaceState.xcuserstate │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── SCJSONUtilDemo.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── qianlongxu.xcuserdatad │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist ├── Podfile.lock ├── Podfile └── SCJSONUtilTests │ ├── Info.plist │ └── SCJSONUtilTests.m ├── SCJSONUtil ├── internal │ ├── scutil.h │ └── scutil.c ├── SCModelUtil.h ├── SCJSONUtil.h ├── SCModelUtil.m └── SCJSONUtil.m ├── .travis.yml ├── SCJSONUtil.podspec ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | SCJSONUtilDemo/SCJSONUtilDemo.xcodeproj/project.xcworkspace/xcuserdata 2 | Demo/SCJSONUtilDemo.xcworkspace/xcuserdata 3 | Demo/Pods 4 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SCJSONUtil/SCJSONUtil.modulemap: -------------------------------------------------------------------------------- 1 | framework module SCJSONUtil { 2 | umbrella header "SCJSONUtil-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SCJSONUtil/SCJSONUtil-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SCJSONUtil : NSObject 3 | @end 4 | @implementation PodsDummy_SCJSONUtil 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/jsonfiles/VideoInfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "欢乐颂", 3 | "data" : { 4 | "cks": [1,2,3], 5 | "hcs": ["a","b","c"], 6 | "dus": ["15","16","17"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcodeproj/xcuserdata/qianlongxu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/jsonfiles/Store.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "测试下KeyPath", 3 | "data" : { 4 | "order": [1,2,3], 5 | "category": ["蔬菜","水果","肉食"], 6 | "weight": ["15","16","17"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-SCJSONUtilDemo/Pods-SCJSONUtilDemo.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_SCJSONUtilDemo { 2 | umbrella header "Pods-SCJSONUtilDemo-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-SCJSONUtilDemo/Pods-SCJSONUtilDemo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_SCJSONUtilDemo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_SCJSONUtilDemo 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcodeproj/project.xcworkspace/xcuserdata/crown.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debugly/SCJSONUtil/HEAD/Demo/SCJSONUtilDemo.xcodeproj/project.xcworkspace/xcuserdata/crown.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/Car.m: -------------------------------------------------------------------------------- 1 | // 2 | // Car.m 3 | // QLJSON2Model 4 | // 5 | // Created by xuqianlong on 2016/12/18. 6 | // Copyright © 2016年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import "Car.h" 10 | 11 | @implementation Car 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcodeproj/project.xcworkspace/xcuserdata/qianlongxu.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debugly/SCJSONUtil/HEAD/Demo/SCJSONUtilDemo.xcodeproj/project.xcworkspace/xcuserdata/qianlongxu.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/FavModel.m: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // FavModel.m 4 | // QLJSON2Model 5 | // 6 | // Created by xuqianlong on 15/7/16. 7 | // Copyright (c) 2015年 xuqianlong. All rights reserved. 8 | // 9 | 10 | #import "FavModel.h" 11 | 12 | @implementation FavModel 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/GalleryModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // GalleryModel.m 3 | // QLJSON2Model 4 | // 5 | // Created by xuqianlong on 15/9/8. 6 | // Copyright © 2015年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import "GalleryModel.h" 10 | 11 | @implementation GalleryModel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SCJSONUtil/SCJSONUtil-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /SCJSONUtil/internal/scutil.h: -------------------------------------------------------------------------------- 1 | // 2 | // scutil.h 3 | // SCJSONUtil 4 | // 5 | // Created by qianlongxu on 2020/11/15. 6 | // 7 | 8 | #ifndef scutil_h 9 | #define scutil_h 10 | 11 | #include 12 | #include 13 | 14 | bool QLCStrEqual(char *v1,char *v2); 15 | 16 | #endif /* scutil_h */ 17 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // SCJSONUtilDemo 4 | // 5 | // Created by xuqianlong on 2017/7/21. 6 | // Copyright © 2017年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SCJSONUtil/internal/scutil.c: -------------------------------------------------------------------------------- 1 | // 2 | // scutil.c 3 | // SCJSONUtil 4 | // 5 | // Created by qianlongxu on 2020/11/15. 6 | // 7 | 8 | #include "scutil.h" 9 | #include 10 | 11 | bool QLCStrEqual(char *v1,char *v2) { 12 | if (NULL == v1 || NULL == v2) { 13 | return 0; 14 | } 15 | return 0 == strcmp(v1, v2); 16 | } 17 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SCJSONUtil (2.5.6) 3 | 4 | DEPENDENCIES: 5 | - SCJSONUtil (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | SCJSONUtil: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | SCJSONUtil: 0e8be7fd76b0aadcedd974f77e86981ec71a0ece 13 | 14 | PODFILE CHECKSUM: 3b4591dd2be1e6d0a179416cc51e1c04038e2832 15 | 16 | COCOAPODS: 1.16.1 17 | -------------------------------------------------------------------------------- /Demo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SCJSONUtil (2.5.6) 3 | 4 | DEPENDENCIES: 5 | - SCJSONUtil (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | SCJSONUtil: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | SCJSONUtil: 0e8be7fd76b0aadcedd974f77e86981ec71a0ece 13 | 14 | PODFILE CHECKSUM: 3b4591dd2be1e6d0a179416cc51e1c04038e2832 15 | 16 | COCOAPODS: 1.16.1 17 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/jsonfiles/Car.json: -------------------------------------------------------------------------------- 1 | { 2 | "b":true, 3 | "b1":-1, 4 | "b2":"1", 5 | "pStorng": 6 | { 7 | "refContent":3333333, 8 | "nameEN":"TopShop", 9 | "nameCN":"TopShop", 10 | "desc":"描述摘要", 11 | "pic": "http://pic14.shangpin.com/e/s/15/04/01/20150401185027193594-10-10.jpg" 12 | }, 13 | "pWeak" :123 14 | } 15 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SCJSONUtilDemo 4 | // 5 | // Created by xuqianlong on 2017/7/21. 6 | // Copyright © 2017年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SCJSONUtilDemo 4 | // 5 | // Created by xuqianlong on 2017/7/21. 6 | // Copyright © 2017年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-SCJSONUtilDemo/Pods-SCJSONUtilDemo-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_SCJSONUtilDemoVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_SCJSONUtilDemoVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Demo/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '12.0' 3 | 4 | target 'SCJSONUtilDemo' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | pod 'SCJSONUtil',:path => '../' 9 | 10 | end 11 | 12 | target 'SCJSONUtilTests' do 13 | # Comment the next line if you don't want to use dynamic frameworks 14 | use_frameworks! 15 | 16 | pod 'SCJSONUtil',:path => '../' 17 | 18 | end -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/OCTypes.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCTypes.m 3 | // SCJSONUtilDemo 4 | // 5 | // Created by qianlongxu on 2019/11/26. 6 | // Copyright © 2019 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import "OCTypes.h" 10 | 11 | @implementation OCClassA 12 | 13 | @end 14 | 15 | @implementation OCTypes 16 | 17 | - (instancetype)init 18 | { 19 | self = [super init]; 20 | if (self) { 21 | _skipMe = @"我没有被解析"; 22 | } 23 | return self; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SCJSONUtil/SCJSONUtil-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "SCJSONUtil.h" 14 | #import "SCModelUtil.h" 15 | 16 | FOUNDATION_EXPORT double SCJSONUtilVersionNumber; 17 | FOUNDATION_EXPORT const unsigned char SCJSONUtilVersionString[]; 18 | 19 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcodeproj/xcuserdata/matt.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SCJSONUtilDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 3 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/StoreModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // StoreModel.m 3 | // SCJSONUtilDemo 4 | // 5 | // Created by Matt Reach on 2020/11/13. 6 | // Copyright © 2020 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import "StoreModel.h" 10 | 11 | @implementation StoreModel 12 | 13 | - (NSDictionary *)sc_collideKeysMap 14 | { 15 | return @{ 16 | @"data.order":@"order", 17 | @"data.category":@"category", 18 | @"data.weight":@"weight", 19 | }; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcodeproj/xcuserdata/qianlongxu.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SCJSONUtilDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/FavModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // FavModel.h 3 | // QLJSON2Model 4 | // 5 | // Created by xuqianlong on 15/7/16. 6 | // Copyright (c) 2015年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FavModel : NSObject 12 | 13 | @property (nonatomic, assign) long refContent; 14 | @property (nonatomic, copy) NSString *nameEN; 15 | @property (nonatomic, copy) NSString *nameCN; 16 | @property (nonatomic, copy) NSURL *pic; //这个需要手动transfer 17 | @property (nonatomic, copy) NSString *desc; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SCJSONUtil/SCJSONUtil.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SCJSONUtil 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "Foundation" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/Test.h: -------------------------------------------------------------------------------- 1 | // 2 | // Test.h 3 | // JSONUtilDemo 4 | // 5 | // Created by Reach Matt on 2025/4/1. 6 | // 7 | 8 | #import 9 | 10 | @interface Test : NSObject 11 | 12 | + (NSString *)testAll; 13 | + (void)printTypeEncodings; 14 | + (NSString *)testOCTypes; 15 | + (NSString *)testKeyPathFromDictionary; 16 | + (NSString *)testModelFromDictionary; 17 | + (NSString *)testModelsFromJSONArr; 18 | + (NSString *)testKeyPath; 19 | + (NSString *)testDynamicConvertFromDictionary; 20 | + (NSString *)testCustomConvertFromDictionary; 21 | + (void)testPerformance; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/StoreModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // StoreModel.h 3 | // SCJSONUtilDemo 4 | // 5 | // Created by Matt Reach on 2020/11/13. 6 | // Copyright © 2020 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SCJSONUtil.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface StoreModel : NSObject 15 | 16 | @property (nonatomic, copy) NSString *name; 17 | @property (nonatomic, strong) NSArray *order; 18 | @property (nonatomic, strong) NSArray *category; 19 | @property (nonatomic, strong) NSArray *weight; 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /SCJSONUtil/SCModelUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCModelUtil.h 3 | // SCJSONUtil 4 | // 5 | // Created by qianlongxu on 2020/11/15. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface NSObject(SCModel2JSON) 13 | 14 | /** 15 | * @brief:将 model 对象转成 json 对象 16 | */ 17 | - (id)sc_toJSON; 18 | 19 | /** 20 | * @brief:将 model 对象转成 json 对象,字典key值有所不同 21 | * 22 | * @param printProperyType 23 | * YES:字典key值会带上Modle里定义的类型,用于debug时查看,不适合发送给服务器等场景! 24 | * NO:等同于 sc_toJSON; 25 | */ 26 | - (id)sc_toJSONWithProperyType:(BOOL)printProperyType; 27 | 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode11.3 6 | language: objective-c 7 | # cache: cocoapods 8 | podfile: Demo/Podfile 9 | before_install: 10 | - gem install cocoapods # Since Travis is not always on latest version 11 | - pod install --project-directory=Demo 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Demo/SCJSONUtilDemo.xcworkspace -scheme SCJSONUtilTests -destination 'platform=iOS Simulator,OS=13.3,name=iPhone 8' ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint --allow-warnings 15 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcodeproj/xcuserdata/crown.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SCJSONUtilDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | C342B29A1F218554007F04FC 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/util/Util.h: -------------------------------------------------------------------------------- 1 | // 2 | // Util.h 3 | // SCJSONUtilDemo 4 | // 5 | // Created by Matt Reach on 2020/11/13. 6 | // Copyright © 2020 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface Util : NSObject 14 | 15 | + (NSDictionary *)readOCTypes; 16 | 17 | + (NSDictionary *)readCarInfo; 18 | 19 | + (NSDictionary *)readUserInfo; 20 | 21 | + (NSArray *)readFavConcern; 22 | 23 | + (NSDictionary *)readGalleryList; 24 | 25 | + (NSDictionary *)readVideoList; 26 | 27 | + (NSDictionary *)readVideoInfo; 28 | 29 | + (NSDictionary *)readStore; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcodeproj/project.xcworkspace/xcuserdata/qianlongxu.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | EnabledFullIndexStoreVisibility 12 | 13 | IssueFilterStyle 14 | ShowActiveSchemeOnly 15 | LiveSourceIssuesEnabled 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Demo/Pods/Pods.xcodeproj/xcuserdata/qianlongxu.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-SCJSONUtilDemo.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-SCJSONUtilTests.xcscheme 13 | 14 | isShown 15 | 16 | 17 | SCJSONUtil.xcscheme 18 | 19 | isShown 20 | 21 | 22 | 23 | SuppressBuildableAutocreation 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/VideoInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // VideoInfo.h 3 | // SCJSONUtilDemo 4 | // 5 | // Created by Matt Reach on 2019/2/28. 6 | // Copyright © 2019 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SCJSONUtil.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface VideoSection : NSObject 15 | 16 | @property (nonatomic, assign) int ck; 17 | @property (nonatomic, copy) NSString *hc; 18 | @property (nonatomic, copy) NSString *du; 19 | 20 | @end 21 | 22 | @interface VideoInfo : NSObject 23 | 24 | @property (nonatomic, copy) NSString *name; 25 | @property (nonatomic, strong) NSArray *sections; 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/GalleryModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // GalleryModel.h 3 | // QLJSON2Model 4 | // 5 | // Created by xuqianlong on 15/9/8. 6 | // Copyright © 2015年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GalleryModel : NSObject 12 | 13 | @property (nonatomic, assign) BOOL isFlagship; 14 | @property (nonatomic, copy) NSString *name; 15 | @property (nonatomic, copy) NSURL *pic; 16 | @property (nonatomic, copy) NSString *refContent; 17 | @property (nonatomic, copy) NSString *type; 18 | 19 | @end 20 | 21 | /* 22 | "isFlagship": "0", 23 | "name": "白色情人节 与浪漫牵手", 24 | "pic": "http://pic16.shangpin.com/e/s/15/03/06/20150306174649601525-10-10.jpg", 25 | "refContent": "http://m.shangpin.com/meet/189", 26 | "type": "5" 27 | 28 | */ 29 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilTests/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/jsonfiles/OCTypes.json: -------------------------------------------------------------------------------- 1 | { 2 | "boolType": true, 3 | "BOOLType": true, 4 | "charType": 32, 5 | "uCharType": 97, 6 | "shortType": 123, 7 | "uShortType": 234, 8 | "intType": 345, 9 | "uIntType": 456, 10 | "longType": 567, 11 | "uLongType": 789, 12 | "longlongType": 5677, 13 | "uLongLongType": 7899, 14 | "floatType": 345.123, 15 | "doubleType": 456.12345, 16 | "stringType": "I'm a string.", 17 | "mutableStringType": "I'm a mutable string.", 18 | "numberType": "7982", 19 | "urlType": "https://debugly.cn", 20 | "fileURLType": "file:///Users/qianlongxu/Desktop/Caption.zip", 21 | "classAType": { 22 | "name": "I'm a classA instance." 23 | }, 24 | "idType": "I'm id type.", 25 | "hash": 979798, 26 | "superclass": "NSObject", 27 | "description": "I'm a description", 28 | "debugDescription": "I'm a debug description" 29 | } 30 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/SCJSONUtil/SCJSONUtil-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | ${PODS_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 | FMWK 17 | CFBundleShortVersionString 18 | 2.5.6 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-SCJSONUtilDemo/Pods-SCJSONUtilDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | ${PODS_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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // SCJSONUtilDemo 4 | // 5 | // Created by xuqianlong on 2017/7/21. 6 | // Copyright © 2017年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "Test.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (weak, nonatomic) IBOutlet UITextView *txv; 15 | 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | 23 | [Test testPerformance]; 24 | [Test printTypeEncodings]; 25 | 26 | self.txv.text = [Test testAll]; 27 | // self.txv.text = [Test testOCTypes]; 28 | // self.txv.text = [Test testKeyPathFromDictionary]; 29 | // self.txv.text = [Test testModelFromDictionary]; 30 | // self.txv.text = [Test testModelsFromJSONArr]; 31 | // self.txv.text = [Test testKeyPath]; 32 | // self.txv.text = [Test testDynamicConvertFromDictionary]; 33 | // self.txv.text = [Test testCustomConvertFromDictionary]; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcworkspace/xcuserdata/qianlongxu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/DynamicVideos.m: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicVideos.m 3 | // SCJSONUtilDemo 4 | // 5 | // Created by Matt Reach on 2019/1/16. 6 | // Copyright © 2019 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import "DynamicVideos.h" 10 | 11 | @implementation VideoItem 12 | 13 | @end 14 | 15 | @implementation DynamicVideos 16 | 17 | - (NSDictionary *)sc_collideKeysMap 18 | { 19 | return @{@"wp" : @"wps",@"activity.on" : @"activity"}; 20 | } 21 | 22 | - (NSDictionary *)sc_collideKeyModelMap 23 | { 24 | return @{@"videos":@"VideoItem"}; 25 | } 26 | 27 | - (BOOL)sc_unDefinedKey:(NSString **)keyPtr forValue:(id *)valuePtr refObj:(id)refObj 28 | { 29 | NSDictionary *refDic = refObj; 30 | NSString *key = *keyPtr; 31 | NSString *mapedKey = [refDic objectForKey:key]; 32 | if (mapedKey) { 33 | *keyPtr = mapedKey; 34 | NSDictionary *value = *valuePtr; 35 | if ([value isKindOfClass:[NSDictionary class]]) { 36 | NSArray *videos = value[@"videos"]; 37 | *valuePtr = videos; 38 | } 39 | return YES; 40 | } 41 | return NO; 42 | } 43 | @end 44 | -------------------------------------------------------------------------------- /SCJSONUtil.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint SCJSONUtil.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | s.name = "SCJSONUtil" 12 | s.version = "2.5.7" 13 | s.summary = "轻量但功能强大的 JSON 转 Model 框架,支持 iOS/macOS/tvOS 平台" 14 | 15 | s.description = <<-DESC 16 | 轻量但功能强大的 JSON 转 Model 框架,支持 iOS/macOS/tvOS 平台 17 | DESC 18 | 19 | s.homepage = "https://github.com/debugly/SCJSONUtil" 20 | s.license = "Apache License" 21 | s.author = { "qianlongxu" => "qianlongxu@gmail.com" } 22 | 23 | s.ios.deployment_target = "10.0" 24 | s.tvos.deployment_target = "10.0" 25 | s.osx.deployment_target = "10.11" 26 | 27 | s.source = { :git => "https://github.com/debugly/SCJSONUtil.git", :tag => "#{s.version}" } 28 | s.source_files = "SCJSONUtil/**/*.{h,m,c}" 29 | s.private_header_files = "SCJSONUtil/internal/*.h" 30 | s.framework = "Foundation" 31 | s.requires_arc = true 32 | 33 | end 34 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/jsonfiles/FavConcern.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "refContent": "111111111", 4 | "nameEN": "TopShopEN1", 5 | "nameCN": "TopShopCN1", 6 | "desc": "描述摘要1", 7 | "pic": "http://pic.shangpin.com/1.jpg" 8 | },{ 9 | "refContent": "222222222", 10 | "nameEN": "TopShopEN2", 11 | "nameCN": "TopShopCN2", 12 | "desc": "描述摘要2", 13 | "pic": "http://pic.shangpin.com/2.jpg" 14 | },{ 15 | "refContent": "333333333", 16 | "nameEN": "TopShopEN3", 17 | "nameCN": "TopShopCN3", 18 | "desc": "描述摘要3", 19 | "pic": "http://pic.shangpin.com/3.jpg" 20 | },{ 21 | "refContent": "444444444", 22 | "nameEN": "TopShopEN4", 23 | "nameCN": "TopShopCN4", 24 | "desc": "描述摘要4", 25 | "pic": "http://pic.shangpin.com/4.jpg" 26 | },{ 27 | "refContent": "555555555", 28 | "nameEN": "TopShopEN5", 29 | "nameCN": "TopShopCN5", 30 | "desc": "描述摘要5", 31 | "pic": "http://pic.shangpin.com/5.jpg" 32 | },{ 33 | "refContent": "666666666", 34 | "nameEN": "TopShopEN6", 35 | "nameCN": "TopShopCN6", 36 | "desc": "描述摘要6", 37 | "pic": "http://pic.shangpin.com/6.jpg" 38 | } 39 | ] 40 | 41 | 42 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/UserInfoModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfoModel.m 3 | // QLJSON2Model 4 | // 5 | // Created by xuqianlong on 15/7/14. 6 | // Copyright (c) 2015年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import "UserInfoModel.h" 10 | #import "SCJSONUtil.h" 11 | 12 | @implementation CarInfoModel 13 | 14 | - (NSDictionary *)sc_collideKeysMap 15 | { 16 | return @{@"brand_img_url":@"brandImg"}; 17 | } 18 | 19 | @end 20 | 21 | @implementation AvatarInfoModel 22 | 23 | @end 24 | 25 | @implementation BasicInfoModel 26 | 27 | - (NSDictionary *)sc_collideKeysMap 28 | { 29 | return @{@"avatar_img":@"avatarInfo"}; 30 | } 31 | 32 | //给name加个前缀; 33 | 34 | - (id)sc_key:(NSString *)key beforeAssignedValue:(NSString *)value 35 | { 36 | if ([key isEqualToString:@"name"]) { 37 | if ([value isKindOfClass:[NSString class]]) { 38 | return [@"xql." stringByAppendingString:value]; 39 | } 40 | return nil; 41 | } 42 | return value; 43 | } 44 | 45 | @end 46 | 47 | @implementation DataInfoModel 48 | 49 | - (NSDictionary *)sc_collideKeyModelMap 50 | { 51 | return @{@"cars":@"CarInfoModel"}; 52 | } 53 | 54 | - (NSDictionary *)sc_collideKeysMap 55 | { 56 | return @{@"basic":@"basicInfo",@"cars":@"carInfoArr"}; 57 | } 58 | 59 | @end 60 | 61 | @implementation UserInfoModel 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/DynamicVideos.h: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicVideos.h 3 | // SCJSONUtilDemo 4 | // 5 | // Created by Matt Reach on 2019/1/16. 6 | // Copyright © 2019 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface VideoItem : NSObject 14 | 15 | @property (nonatomic,assign) long no; 16 | @property (nonatomic,copy) NSString *showName; 17 | @property (nonatomic,copy) NSString *title; 18 | @property (nonatomic,copy) NSString *domain; 19 | @property (nonatomic,strong) NSNumber *type; 20 | @property (nonatomic,strong) NSNumber *rid; 21 | @property (nonatomic,strong) NSNumber *uid; 22 | @property (nonatomic,copy) NSMutableString *rSwfurl; 23 | @property (nonatomic,copy) NSURL *url; 24 | 25 | @end 26 | 27 | @interface DynamicVideos : NSObject 28 | 29 | @property (nonatomic,copy) NSString *area; 30 | @property (nonatomic,assign) long playlistid; 31 | @property (nonatomic,strong) NSArray *videos; 32 | ///没有指定model,原值输出! 33 | @property (nonatomic,strong) NSArray *wps; 34 | ///与服务器返回类型不同,也不能自动转换,因此不能解析! 35 | @property (nonatomic,assign) int array; 36 | @property (nonatomic,assign) BOOL activity; 37 | @property (nonatomic,copy) NSURL * smallVerPicUrl; 38 | @property (nonatomic,copy) NSMutableString * showAlbumName; 39 | 40 | @end 41 | 42 | NS_ASSUME_NONNULL_END 43 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/UserInfoModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfoModel.h 3 | // QLJSON2Model 4 | // 5 | // Created by xuqianlong on 15/7/14. 6 | // Copyright (c) 2015年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AvatarInfoModel : NSObject 12 | 13 | @property (nonatomic,copy) NSString *original_url; 14 | @property (nonatomic,copy) NSString *thumbnail_url; 15 | @property (nonatomic,copy) NSURL *raw_url; 16 | 17 | @end 18 | 19 | @interface CarInfoModel : NSObject 20 | 21 | @property (nonatomic,copy) NSString *bought_time; 22 | @property (nonatomic,copy) NSString *brand; 23 | @property (nonatomic,retain) AvatarInfoModel *brandImg; 24 | 25 | @end 26 | 27 | @interface BasicInfoModel : NSObject 28 | 29 | @property (nonatomic,retain) AvatarInfoModel *avatarInfo; 30 | @property (nonatomic,copy) NSString *gender; 31 | @property (nonatomic,assign) long uid; 32 | @property (nonatomic,copy) NSString *name; 33 | @property (nonatomic,copy) NSString *phone_number; 34 | 35 | @end 36 | 37 | @interface DataInfoModel : NSObject 38 | 39 | @property (nonatomic,retain) BasicInfoModel *basicInfo; 40 | @property (nonatomic,retain) NSMutableArray *carInfoArr; 41 | 42 | @end 43 | 44 | @interface UserInfoModel : NSObject 45 | 46 | @property (nonatomic,copy) NSString *code; 47 | @property (nonatomic,retain) DataInfoModel *data; 48 | @property (nonatomic,copy) NSString *test; 49 | 50 | @end 51 | 52 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/OCTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCTypes.h 3 | // SCJSONUtilDemo 4 | // 5 | // Created by qianlongxu on 2019/11/26. 6 | // Copyright © 2019 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface OCClassA : NSObject 12 | 13 | @property (nonatomic, copy) NSString * name; 14 | 15 | @end 16 | 17 | @interface OCTypes : NSObject 18 | 19 | @property (nonatomic, assign) bool boolType; 20 | @property (nonatomic, assign) BOOL BOOLType; 21 | @property (nonatomic, assign) char charType; 22 | @property (nonatomic, assign) unsigned char uCharType; 23 | @property (nonatomic, assign) short shortType; 24 | @property (nonatomic, assign) unsigned short uShortType; 25 | @property (nonatomic, assign) int intType; 26 | @property (nonatomic, assign) unsigned int uIntType; 27 | @property (nonatomic, assign) long longType; 28 | @property (nonatomic, assign) unsigned long uLongType; 29 | @property (nonatomic, assign) long long longlongType; 30 | @property (nonatomic, assign) unsigned long long uLongLongType; 31 | @property (nonatomic, assign) float floatType; 32 | @property (nonatomic, assign) double doubleType; 33 | 34 | @property (nonatomic, copy) NSString * stringType; 35 | @property (nonatomic, copy) NSMutableString * mutableStringType; 36 | @property (nonatomic, strong) NSNumber * numberType; 37 | @property (nonatomic, strong) NSURL * urlType; 38 | @property (nonatomic, strong) NSURL * fileURLType; 39 | @property (nonatomic, strong) OCClassA * classAType; 40 | @property (nonatomic, strong) id idType; 41 | //只读属性不解析 42 | @property (readonly, strong) NSString *skipMe; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/util/Util.m: -------------------------------------------------------------------------------- 1 | // 2 | // Util.m 3 | // SCJSONUtilDemo 4 | // 5 | // Created by Matt Reach on 2020/11/13. 6 | // Copyright © 2020 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import "Util.h" 10 | 11 | @implementation Util 12 | 13 | + (NSString *)jsonFilePath:(NSString *)fName 14 | { 15 | return [[NSBundle mainBundle]pathForResource:fName ofType:@"json"]; 16 | } 17 | 18 | + (id)readBundleJSONFile:(NSString *)fName 19 | { 20 | NSData *data = [NSData dataWithContentsOfFile:[self jsonFilePath:fName]]; 21 | NSError *err = nil; 22 | id json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err]; 23 | if (err) { 24 | return nil; 25 | } 26 | return json; 27 | } 28 | 29 | + (NSDictionary *)readOCTypes 30 | { 31 | return [self readBundleJSONFile:@"OCTypes"]; 32 | } 33 | 34 | + (NSDictionary *)readCarInfo 35 | { 36 | return [self readBundleJSONFile:@"Car"]; 37 | } 38 | 39 | + (NSDictionary *)readUserInfo 40 | { 41 | return [self readBundleJSONFile:@"Userinfo"]; 42 | } 43 | 44 | + (NSArray *)readFavConcern 45 | { 46 | return [self readBundleJSONFile:@"FavConcern"]; 47 | } 48 | 49 | + (NSDictionary *)readGalleryList 50 | { 51 | return [self readBundleJSONFile:@"GalleryList"]; 52 | } 53 | 54 | + (NSDictionary *)readVideoList 55 | { 56 | return [self readBundleJSONFile:@"DynamicVideos"]; 57 | } 58 | 59 | + (NSDictionary *)readVideoInfo 60 | { 61 | return [self readBundleJSONFile:@"VideoInfo"]; 62 | } 63 | 64 | + (NSDictionary *)readStore 65 | { 66 | return [self readBundleJSONFile:@"Store"]; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | UIRequiresFullScreen 32 | 33 | UIStatusBarHidden~ipad 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | UISupportedInterfaceOrientations~ipad 41 | 42 | UIInterfaceOrientationLandscapeRight 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/VideoInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // VideoInfo.m 3 | // SCJSONUtilDemo 4 | // 5 | // Created by Matt Reach on 2019/2/28. 6 | // Copyright © 2019 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import "VideoInfo.h" 10 | 11 | @implementation VideoSection 12 | 13 | @end 14 | 15 | @implementation VideoInfo 16 | 17 | - (void)sc_willFinishConvert:(id)data refObj:(id)refObj 18 | { 19 | if([data isKindOfClass:[NSDictionary class]]){ 20 | NSDictionary *dic = (NSDictionary *)data; 21 | NSDictionary *sectionData = dic[@"data"]; 22 | if (sectionData) { 23 | NSArray *cks = sectionData[@"cks"]; 24 | NSArray *hcs = sectionData[@"hcs"]; 25 | NSArray *dus = sectionData[@"dus"]; 26 | if ([cks isKindOfClass:[NSArray class]] && [hcs isKindOfClass:[NSArray class]] && [dus isKindOfClass:[NSArray class]]) { 27 | 28 | if ([cks count] == [hcs count] && [hcs count] == [dus count]) { 29 | NSMutableArray *secArr = [NSMutableArray arrayWithCapacity:3]; 30 | for (int i = 0 ; i < [cks count]; i++) { 31 | id ck = cks[i]; 32 | id hc = hcs[i]; 33 | id du = dus[i]; 34 | [secArr addObject:@{ 35 | @"ck":ck, 36 | @"hc":hc, 37 | @"du":du 38 | }]; 39 | } 40 | 41 | NSArray * videoSecs = [VideoSection sc_instanceArrFormArray:secArr]; 42 | self.sections = videoSecs; 43 | } 44 | } 45 | } 46 | } 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/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 | 27 | 28 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/jsonfiles/Userinfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "code" : "10000", 3 | "test" : "testValue", 4 | "data" :{ 5 | "basic":{ 6 | "gender":"男", 7 | "uid":209849, 8 | "name":"芒果", 9 | "phone_number":"999198838229", 10 | "avatar_img":{ 11 | "original_url":"http://original/e/s/15/04/01/20150401185027193594-10-10.jpg", 12 | "thumbnail_url":"http://thumbnail/e/s/15/04/01/20150401185027193594-10-10.jpg", 13 | "raw_url":"http://raw/e/s/15/04/01/20150401185027193594-10-10.jpg" 14 | } 15 | }, 16 | "cars":[ 17 | { 18 | "bought_time":"2002", 19 | "brand":"werty", 20 | "brand_img_url":{ 21 | "original_url":"http://original/e/s/15/04/01/20150401185027193594-10-10.jpg", 22 | "thumbnail_url":"http://thumbnail/e/s/15/04/01/20150401185027193594-10-10.jpg", 23 | "raw_url":"http://raw/e/s/15/04/01/20150401185027193594-10-10.jpg" 24 | } 25 | }, 26 | { 27 | "bought_time":"2002", 28 | "brand":"werty", 29 | "brand_img_url":{ 30 | "original_url":"http://original/e/s/15/04/01/20150401185027193594-10-10.jpg", 31 | "thumbnail_url":"http://thumbnail/e/s/15/04/01/20150401185027193594-10-10.jpg", 32 | "raw_url":"http://raw/e/s/15/04/01/20150401185027193594-10-10.jpg" 33 | } 34 | }, 35 | { 36 | "bought_time":"2002", 37 | "brand":"werty", 38 | "brand_img_url":{ 39 | "original_url":"http://original/e/s/15/04/01/20150401185027193594-10-10.jpg", 40 | "thumbnail_url":"http://thumbnail/e/s/15/04/01/20150401185027193594-10-10.jpg", 41 | "raw_url":"http://raw/e/s/15/04/01/20150401185027193594-10-10.jpg" 42 | } 43 | } 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SCJSONUtilDemo 4 | // 5 | // Created by xuqianlong on 2017/7/21. 6 | // Copyright © 2017年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/models/Car.h: -------------------------------------------------------------------------------- 1 | // 2 | // Car.h 3 | // QLJSON2Model 4 | // 5 | // Created by xuqianlong on 2016/12/18. 6 | // Copyright © 2016年 xuqianlong. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "FavModel.h" 11 | #import "objc/runtime.h" 12 | @interface Car : NSObject 13 | 14 | @property (nonatomic, strong) FavModel *pStorng; 15 | @property (nonatomic, weak) NSNumber *pWeak; 16 | @property (nonatomic, retain) NSMutableArray *pRetain; 17 | @property (nonatomic, copy) NSString *pCopy; 18 | @property (nonatomic, assign) NSArray *pAssign; 19 | @property (nonatomic, readonly) NSArray *pReadonly; 20 | @property (nonatomic, readonly,getter=isReadonly) NSArray *pReadonlyGetter; 21 | @property (atomic, assign) NSDictionary *pAutomicAssign; 22 | @property (atomic, retain) NSDictionary *pAutomicRetain; 23 | @property (nonatomic, retain) NSMutableArray * pFanXing; 24 | 25 | @property (nonatomic, strong) id pID; 26 | @property (nonatomic, strong) dispatch_block_t pBlock; 27 | @property (nonatomic, strong) Class pClass; 28 | @property (nonatomic, assign) SEL pSEL; 29 | @property (nonatomic, assign) Method pMethod; 30 | 31 | @property (nonatomic, assign) float pFloat; 32 | @property (nonatomic, assign) double pDouble; 33 | 34 | @property (nonatomic, assign) Byte pByte; 35 | @property (nonatomic, assign) char pChar; 36 | @property (nonatomic, assign) short pShort; 37 | @property (nonatomic, assign) int pInt; 38 | @property (nonatomic, assign) long pLong; 39 | @property (nonatomic, assign) long long pLongLong; 40 | @property (nonatomic, assign) BOOL pBool; 41 | @property (nonatomic, assign) bool pbool; 42 | 43 | @property (nonatomic, assign) bool b; 44 | @property (nonatomic, assign) bool b1; 45 | @property (nonatomic, assign) bool b2; 46 | 47 | @end 48 | /* 49 | T@"FavModel",&,N,V_pStorng 50 | T@"NSNumber",W,N,V_pWeak 51 | T@"NSMutableArray",&,N,V_pRetain 52 | T@"NSString",C,N,V_pCopy 53 | T@"NSArray",N,V_pAssign 54 | T@"NSArray",R,N,V_pReadonly 55 | T@"NSArray",R,N,GisReadonly,V_pReadonlyGetter 56 | T@"NSDictionary",V_pAutomicAssign 57 | T@"NSDictionary",&,V_pAutomicRetain 58 | T@,&,N,V_pID 59 | T@?,C,N,V_pBlock 60 | T#,&,N,V_pClass 61 | T:,N,V_pSEL 62 | T^{objc_method=},N,V_pMethod 63 | Tf,N,V_pFloat 64 | Td,N,V_pDouble 65 | TC,N,V_pByte 66 | Tc,N,V_pChar 67 | Ts,N,V_pShort 68 | Ti,N,V_pInt 69 | Tq,N,V_pLong 70 | Tq,N,V_pLongLong 71 | TB,N,V_pBool 72 | TB,N,V_pbool 73 | */ 74 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/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 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo.xcodeproj/xcuserdata/crown.xcuserdatad/xcschemes/SCJSONUtilDemo.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 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/jsonfiles/DynamicVideos.json: -------------------------------------------------------------------------------- 1 | { 2 | "playlistid": 5828854, 3 | "area": "内地", 4 | "activity": { 5 | "on": 1 6 | }, 7 | "array": ["abc"], 8 | "wp": [ 9 | { 10 | "v": "上错车暧昧偶遇,为美人抛弃兄弟", 11 | "k": "102" 12 | }, 13 | { 14 | "v": "试镜失败再失恋,祸不单行患血癌", 15 | "k": "591" 16 | }, 17 | { 18 | "v": "想方设法欲接近,撒娇求助屡碰壁", 19 | "k": "1288" 20 | }, 21 | { 22 | "v": "泳池装死险亲吻,突然求婚做交换", 23 | "k": "1791" 24 | } 25 | ], 26 | "qq": { 27 | "size": 48, 28 | "videos": [ 29 | { 30 | "no": 1, 31 | "uid": 454463456, 32 | "showName": "第1集", 33 | "swfurl": 454463456, 34 | "domain": "qq.com", 35 | "rUrl": "http://v.qq.com/x/page/b0029rfy05v.html?ptag=hu.tv", 36 | "rSwfurl": "http://imgcache.qq.com/tencentvideo_v1/player/TencentPlayer.swf?vid=b0029rfy05v&autoplay=0&list=2&showcfg=1&tpid=1&cid=3p2fnoopmegcajh", 37 | "rid": 5828854, 38 | "title": "大江大河[TV版]_01", 39 | "type": 0, 40 | "url": "http://v.qq.com/x/page/b0029rfy05v.html?ptag=hu.tv" 41 | }, 42 | { 43 | "no": 2, 44 | "uid": 454463457, 45 | "showName": "第2集", 46 | "swfurl": 454463457, 47 | "domain": "qq.com", 48 | "rUrl": "http://v.qq.com/x/page/q0029dl5gs3.html?ptag=hu.tv", 49 | "rSwfurl": "http://imgcache.qq.com/tencentvideo_v1/player/TencentPlayer.swf?vid=q0029dl5gs3&autoplay=0&list=2&showcfg=1&tpid=1&cid=3p2fnoopmegcajh", 50 | "rid": 5828854, 51 | "title": "大江大河[TV版]_02", 52 | "type": 0, 53 | "url": "http://v.qq.com/x/page/q0029dl5gs3.html?ptag=hu.tv" 54 | } 55 | ] 56 | }, 57 | "albumName": "大江大河", 58 | "types": [ 59 | "剧情片" 60 | ], 61 | "author": "", 62 | "smallVerPicUrl": "http://img.tv.itc.cn/soimg/test/default.png", 63 | "directors": [ 64 | "孔笙", 65 | "黄伟" 66 | ], 67 | "updateTime": 1547618953594, 68 | "iqiyi": { 69 | "size": 2, 70 | "videos": [ 71 | { 72 | "no": 1, 73 | "uid": 454463474, 74 | "showName": "第1集", 75 | "swfurl": 454463474, 76 | "domain": "iqiyi.com", 77 | "rUrl": "http://www.iqiyi.com/v_19rqs0we9s.html?vfm=m_312_shsp", 78 | "rid": 5828854, 79 | "title": "大江大河第1集", 80 | "type": 2, 81 | "url": "http://www.iqiyi.com/v_19rqs0we9s.html?vfm=m_312_shsp" 82 | }, 83 | { 84 | "no": 2, 85 | "uid": 454463473, 86 | "showName": "第2集", 87 | "swfurl": 454463473, 88 | "domain": "iqiyi.com", 89 | "rUrl": "http://www.iqiyi.com/v_19rqs0wim0.html?vfm=m_312_shsp", 90 | "rid": 5828854, 91 | "title": "大江大河第2集", 92 | "type": 2, 93 | "url": "http://www.iqiyi.com/v_19rqs0wim0.html?vfm=m_312_shsp" 94 | } 95 | ] 96 | }, 97 | "albumDesc": "宋运辉(王凯饰)天资聪颖,却出身不好,一直倍受歧视,但是他把握住了1978年恢复高考的机会,抓住机遇,勤学苦干,当上了国企的技术人员,一步步晋升,奠定了成功人生的基础,但也在新时代的变革中逐渐迷失。与宋运辉不同的是他的姐夫雷东宝(杨烁 饰)。他出身贫寒、属于苗正根红的“大老粗”,行动力十足。在乡村改革的浪潮中带领村民紧跟政策,一直走在时代的前沿。但由于自身文化水平不高,眼界不够开阔,最终绊倒在新事物脚下。如果说宋运辉和雷东宝的经历是国营经济和集体经济的缩影。那么个体户杨巡(董子健 饰)无疑就是个体经济的典型代表,在翻滚向前的时代中,他手忙脚乱抓住过商机,也踩踏过陷阱,生意场上几经波折,最终拥有了自己的产业,成为了那个时代个体经济的典型代表。", 98 | "categories": [ 99 | "电视剧" 100 | ], 101 | "showAlbumName": "大江大河(48集全)" 102 | } 103 | -------------------------------------------------------------------------------- /SCJSONUtil/SCJSONUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCJSONUtil.h 3 | // 4 | // Created by xuqianlong on 15/9/3. 5 | // Copyright (c) 2015年 Mac. All rights reserved. 6 | // 7 | //https://github.com/debugly/SCJSONUtil 8 | 9 | #import 10 | 11 | ///日志开关,默认关闭 12 | void SCJSONUtilLog(BOOL on); 13 | BOOL isSCJSONUtilLogOn(void); 14 | 15 | @protocol SCJSON2ModelProtocol 16 | 17 | @optional; 18 | /** 19 | * @brief:存放冲突key映射的字典 @{@"server's key":@"model‘s property name"} 20 | * 遍历服务端返回数据,通过key去查找客户端model里定义的属性名 21 | */ 22 | - (NSDictionary *)sc_collideKeysMap; 23 | 24 | /** 25 | * @brief:存放服务端key和映射Model的字典,@{@"server's key":@"your Model Name"} 26 | */ 27 | - (NSDictionary *)sc_collideKeyModelMap; 28 | 29 | /** 30 | 用于解决映射关系不能确定,需要动态修改的情况 31 | 32 | @param keyPtr 服务器key(如果做了映射sc_collideKeysMap,则是映射之后的key);二级指针,可修改内容 33 | @param valuePtr 服务器value;二级指针,可修改内容 34 | @param refObj 客户端解析时传过来的额外信息 35 | @return 36 | YES:动态解决了映射关系,可根据 *keyPtr 查找到属性信息!继续走解析流程; 37 | NO:没有解决,放弃该key-value;中断解析流程,开始解析下一个key; 38 | */ 39 | - (BOOL)sc_unDefinedKey:(NSString **)keyPtr forValue:(id *)valuePtr refObj:(id)refObj; 40 | 41 | /** 42 | * @brief:在给 key 赋值之前,将从字典里找到的 value 返回来,你可以修改这个值,然后返回新的值,从而达到处理业务逻辑的目的; 43 | @parameter key :该model的属性名 44 | @parameter value :从字典里取出来的原始值,还未做自动映射 45 | 框架会将返回值进行自动映射! 46 | */ 47 | - (id)sc_key:(NSString *)key beforeAssignedValue:(id)value refObj:(id)refObj; 48 | 49 | /** 50 | * @brief:JOSN 转 Model即将完成,你可以在这里做最后的自定义解析,以完成复杂的转换! 51 | @parameter data :服务器返回的原始JSON数据; 52 | @parameter refObj :客户端解析时传过来的额外信息; 53 | */ 54 | - (void)sc_willFinishConvert:(id)data refObj:(id)refObj; 55 | 56 | @end 57 | 58 | 59 | @interface NSObject (SCJSON2Model) 60 | 61 | /** 62 | * @brief:创建好对象后调用此方法,将json赋值给该modle对象; 63 | * 64 | * @param jsonDic 与model对应的json 65 | */ 66 | - (void)sc_assembleDataFormDic:(NSDictionary *)jsonDic; 67 | 68 | /** 69 | * @brief:创建一个已经赋过值的model对像; 70 | * if jsonDic is nil or empty return nil; 71 | * 72 | * @param jsonDic 与 modle 属性对应的字典 73 | */ 74 | + (instancetype)sc_instanceFormDic:(NSDictionary *)jsonDic; 75 | 76 | /** 77 | * @brief:jsonArr数组--》model数组,if jsonArr is nil or empty return nil; 78 | */ 79 | + (NSArray *)sc_instanceArrFormArray:(NSArray *)jsonArr; 80 | 81 | /** 82 | * @brief:自动判断Value类型进行解析 83 | * a、if value is array --> a model instances array,if JSON is nil or empty return nil; 84 | * b、if value is dictionary --> a model instance,if JSON is nil or empty return nil; 85 | * c、if caller's Class is NSNumber、NSString、NSURL or NSDecimalNumber ; auto convert value to (the caller's Class) instance. 86 | */ 87 | + (id)sc_instanceFromValue:(id)value; 88 | 89 | @end 90 | 91 | 92 | #pragma mark - JOSNUtil c functions 93 | 94 | /** 95 | @brief 很方便的一个c方法,将 JSON 转为 Model,可用将 JSON 数据解析成 Model 对象! 96 | 97 | @param json 服务器返回的JSON 98 | @param modelName 客户端定义的Model类类名, 或者系统的 NSNumber、NSString、NSURL、NSDecimalNumber 99 | @return Model类的实例对象 100 | */ 101 | FOUNDATION_EXPORT id SCJSON2Model(id json, NSString *modelName); 102 | 103 | /** 104 | @brief 同 SCJSON2Model 105 | @param refObj 客户端传递的额外参数,辅助解析;具体参考 Video.json 解析成 DynamicVideos 的例子! 106 | */ 107 | FOUNDATION_EXPORT id SCJSON2ModelV2(id json, NSString *modelName, id refObj); 108 | 109 | /** 110 | * @brief 根据 pathArr 找到目标 JSON,可辅助 JSON2Model 函数使用,先找到目标 JOSN 再解析; 111 | * @param pathArr 查找目标 JSON 的路径数组;@[@"data"], @[@"data",@"list"] 112 | */ 113 | 114 | FOUNDATION_EXPORT id SCFindJSONwithKeyPathArr(NSArray *pathArr, NSDictionary *json); 115 | 116 | /** 117 | * @brief 根据 keyPath 找到目标 JSON,可辅助 JSON2Model 函数使用,先找到目标 JOSN 再解析; 118 | * @param keyPath 查找目标 JSON 的路径;以 / 为分割的字符;例如:@"data", @"data/list",@"data/detail/price"; 119 | */ 120 | FOUNDATION_EXPORT id SCFindJSONwithKeyPath(NSString *keyPath, NSDictionary *json); 121 | 122 | /** 123 | * @brief see SCFindJSONwithKeyPath 124 | * @param keyPath JSON 数据里的一个路径 125 | * @param separator 指定分割字符; 126 | */ 127 | FOUNDATION_EXPORT id SCFindJSONwithKeyPathV2(NSString *keyPath, NSDictionary *JSON, NSString *separator); 128 | 129 | /** 130 | * @brief 将 JSON 对象转成字符串(url类型不会将 / 转义为 \\/) 131 | * @param json JSON 对像 132 | * @param prettyPrinted 是否需要打印的更漂亮,自动换行、缩进 133 | */ 134 | FOUNDATION_EXPORT NSString *JSON2String(id json, BOOL prettyPrinted); 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SCJSONUtil 2 | ============ 3 | 4 | [![Version](https://img.shields.io/cocoapods/v/SCJSONUtil.svg?style=flat)](https://cocoapods.org/pods/SCJSONUtil) 5 | [![License](https://img.shields.io/cocoapods/l/SCJSONUtil.svg?style=flat)](https://cocoapods.org/pods/SCJSONUtil) 6 | [![Platform](https://img.shields.io/cocoapods/p/SCJSONUtil.svg?style=flat)](https://cocoapods.org/pods/SCJSONUtil) 7 | 8 | ## 特性 9 | 10 | 1. 小巧方便,功能强大 11 | 2. 支持映射属性名(比如服务器返回的是id,我们的model里可定义为uid) 12 | 3. 类型自动匹配(比如服务器返回的是Number,model里定义的是String,那么就会解析为 String) 13 | 14 | ## 使用 CocoaPods 安装 15 | 16 | 在 `Podfile` 文件里添加: 17 | 18 | ``` 19 | target 'TargetName' do 20 | pod 'SCJSONUtil' 21 | end 22 | ``` 23 | 24 | ## 使用说明 25 | 26 | 《一看二建三解析》,直接看代码比较直观: 27 | 28 | * 先看 JOSN ,毕竟我们是要把 JSON 转为 Model 嘛 29 | 30 | ``` 31 | { 32 | "code": "0", 33 | "content": { 34 | "gallery": [ 35 | { 36 | "isFlagship": "0", 37 | "name": "白色情人节 与浪漫牵手", 38 | "pic": "http://pic16.shangpin.com/e/s/15/03/06/20150306174649601525-10-10.jpg", 39 | "refContent": "http://m.shangpin.com/meet/189", 40 | "type": "5" 41 | }, 42 | ... 43 | ] 44 | } 45 | } 46 | ``` 47 | 48 | * 根据服务器的json(嵌套关系)建立 model (子model); 49 | 50 | ``` 51 | @interface GalleryModel : NSObject 52 | 53 | @property (nonatomic, copy) NSString *isFlagship; 54 | @property (nonatomic, copy) NSString *name; 55 | @property (nonatomic, copy) NSString *pic; 56 | @property (nonatomic, copy) NSString *refContent; 57 | @property (nonatomic, copy) NSString *type; 58 | 59 | @end 60 | ``` 61 | 62 | * 使用JSONUtil解析 63 | 64 | ``` 65 | 假设responseJSON是服务器返回的json数据,常规的写法是先判断 code 是否等于 0,然后再取出 gallery 对应的json 转成 model; 然而 SCJSONUtil 66 | 提供了更加便捷的方法完成这些步骤: 67 | 68 | //1.根据 keypath 取出目标json 69 | id findedJSON = SCFindJSONwithKeyPath(@"content/gallery", responseJSON); 70 | //2.使用 JSONUtil 解析 71 | NSArray *models = SCJSON2Model(findedJSON, @"GalleryModel"); 72 | //models 就是你想要的GalleryModel数组了! 73 | ``` 74 | 75 | 其他的用法,可以参考demo。 76 | 77 | --- 78 | 79 | ## 核心思想 80 | 81 | 1. 递归:JSON 是可以嵌套的,因此这是一个递归的问题; 82 | 2. 遍历 JSON,根据遍历出的 json 里的key,去 model 里查找对应的映射名或者属性名 83 | 3. 适当的地方进行 ValueTransfer,做到类型自动匹配 84 | 4. 通过 kvc 给 model 赋值 85 | 86 | ### 其他 JSON 转 Model 框架的大致流程 87 | 88 | 这里要解释下第二点,因为这里我和其他的 JSON 转 Model 框架的实现大不相同!先来看下其他的主流思想: 89 | 90 | - 使用 Runtime 获取目标Model类的全部属性! 91 | - 注意这里只能获取到自身的属性,父类的则无法获取到!考虑到继承是我们的一大特性,不应该去限制 Model 类不能有父类(除了NSObject这个基类外),因此需要通过 superClass 去向上遍历,并且遍历到系统类为止,否则的话,可能会获取到一些不是你想要的属性来,可能会为后续工作带来不必要的麻烦。 92 | - 将上一步获取到的**全部属性**缓存到一个全局的区域里,保证下次能够走缓存! 93 | - 因为解析的时候,往往都是给出一个类,而不是给个对象,因此缓存需要跟类挂钩,比如定义一个字典,将类名作为key值,value 则是需要缓存的属性;具体做法也很简单,我曾经写过一篇剖析关联引用底层的文章,系统的做法跟这个类似,其实就是通过静态变量来做,然后可以提供类方法访问,类似于Java的类变量。 94 | - 这里说的属性,一般包括属性名,属性类型等字段。 95 | - 拿到Model类的全部属性后开始遍历 96 | - 在遍历的时候,判断一些全局的忽略设置,值的转化处理等。 97 | - 如果值是字典或者数组,则会递归。 98 | 99 | ### JSONUtil 转 Model 流程 100 | 101 | - 遍历服务器返回的JSON 102 | - 根据遍历的key,去model里查询该属性 103 | - 先看看是否做了属性名映射,没映射就用key当作属性名 104 | - 查到了,说明model里定义了该属性,就通过 Runtime 取出属性的类型,解析继续往下走 105 | - 查不到,说明model里不关心该属性,继续遍历 106 | - 如果值是数组或者字典则创建一个数组或者字典对应的Model开始递归 107 | - 通过第二步获取的属性类型和json值类型比较 108 | - 类型一致,则直接 kvc 赋值 109 | - 类型不一致,则先进行转换,然后再 kvc 赋值 110 | 111 | ### JSONUtil 不做 cache 的原因 112 | 113 | 知道了主流做法后,再来看下 JSONUtil 是如何处理的吧,你会发现 JSONUtil 有很有特色,因为随大流地的去模仿做 cache ,遍历 model 的全部属性,之所以没有这么做,是因为我觉得 **没有必要**! 114 | 115 | - 做 cache 是为了**下次**解析更快,你知道将一个json转换出一个model需要多久吗?对于普通JSON其实不用担心的,如果真的担心,何不使用 GCD 去异步线程里做呢?试问你愿意多看1秒钟(异步GCD解析)的loading还是愿意让App卡顿0.5秒钟(主线程解析)? 116 | 117 | - 举个不太恰当的例子哈:如果model是个3G开关,该开关只会在App启动时请求一次,那么cache反而没什么卵用,说不定还会更耗时! 118 | 119 | - 再比如我的解析框架做了cache,解析的速度非常之快,是不是你就敢大胆的在主线程进行所有的JSON解析?你就不怕有大个的JOSN吗? 120 | 121 | - 如果做了cache,并且在异步线程解析,岂不是更快?是的,的确会快一些,那个差别你是感受不到的,**为了你感受不到的快感而去浪费很长的时间层层遍历属性做个cache**,这是不值得的,不知道你是否同意? 122 | 123 | 这是我的做的测试: 124 | 125 | ``` 126 | NSDictionary *userInfoDic = [self readUserInfo]; 127 | 128 | [self testCount:100000 work:^{ 129 | UserInfoModel *uModel = [UserInfoModel instanceFormDic:userInfoDic]; 130 | }]; 131 | // 10000 次转换耗时:0.51412s 132 | // 100000 次转换耗时:4.61152s 133 | ``` 134 | 135 | 也就是说使用 JSONUtil 转换一个嵌套4层(里面也有数组)的 model ,转换一次需要: **0.0514ms**. 136 | 137 | ### 为什么 JSONUtil 选择遍历服务器返回的 JSON ? 138 | 139 | 遍历终究是要做的,有两种做法,一种是遍历 model的所有属性,另一种是遍历 服务器返回的json;究竟如何选择,困扰了我许久,最后我选择了后者,因为前者的代价较高,需要层层向上遍历,最可恶的是**递归的出口不好把握**,因此选择了后者规避了这个问题! 140 | 141 | 另一方面选择后者是因为,根据我解析的习惯是,服务器定义的字段我都会解析,但是有的字段服务器可能不返回,如果按照前者的遍历方式,会增加几次多余的遍历;当然我否定使用后者不会出现多余的遍历,这个定义model的习惯和服务器返回json的空值都有关系! 142 | 143 | ## 版本 144 | 145 | * 1.0 必须继承 QLBaseModel 父类 146 | 147 | * 1.0.1 在属性命名上有限制,对于子model必须自定义属性名才行 148 | 149 | * 1.0.2 去掉了必须自定义属性名的限制 150 | 151 | * 2.0 则去掉了必须继承父类的限制,改名为 JSONUtil 152 | 153 | * 2.1 增加匹配自动类型功能,比如服务器返回的 Number,客户端定义的是 String,那么会帮你自动转为 String 154 | 155 | * 2.2 公司项目也使用这个库,命名规范需要,统一加上 sl(SL)前缀 156 | 157 | * 2.3 项目重构,将该库提取到了通用库中,SL 前缀改为 SC 158 | 159 | * 2.4 支持 CocoaPods 集成 160 | 161 | * 2.4.1 清理没用的方法 162 | 163 | * 2.4.2 当服务器返回数据不能转化为 Number 时,不使用 KVC 赋值 164 | 165 | * 2.4.3 开始在 OS X 平台测试使用 166 | 167 | * 2.4.4 增删类别方法,增加警告信息,创建Model更加省心 168 | 169 | ``` 170 | 做了类型自动映射后,sc_valueNeedTransfer 显得鸡肋,因此去掉了 171 | - (void)sc_valueNeedTransfer; 方法。 172 | 新增 173 | - (id)sc_key:(NSString *)key beforeAssignedValue:(id)value; 174 | //增加警告信息 175 | 2018-11-27 18:30:17.844219+0800 SCJSONUtilDemo[91682:1947248] ⚠️ UserInfoModel 类没有解析 test 属性;如果客户端和服务端key不相同可通过sc_collideKeysMap 做映射!value:testValue 176 | 2018-11-27 18:30:17.844351+0800 SCJSONUtilDemo[91682:1947248] ⚠️⚠️ DataInfoModel 类的 cars 属性没有指定model类名,这会导致解析后数组里的值是原始值,并非model对象!可以通过 sc_collideKeyModelMap 指定 @{@"cars":@"XyzModel"} 177 | ``` 178 | 179 | * 2.4.5 增加类别方法,可动态做 key-value 映射 180 | 181 | * 2.4.6 增加类别方法,可自定义解析过程 182 | 183 | * 2.4.7 修复通过 KVC 给标量赋值为 nil 时导致的崩溃 184 | 185 | * 2.4.8 修复没有配置 Model 名时,解析完毕后,属性值为nil问题 186 | 187 | * 2.4.9 当服务器返回值是数组类型,而 Model 属性不是数组类型时,不进行解析 188 | 189 | * 2.5.0 支持解析 long long 类型,丰富测试 case 190 | 191 | * 2.5.1 支持 fileURL 类型,丰富测试 case 192 | 193 | * 2.5.2 增加日志控制开关,更加灵活 194 | 195 | * 2.5.3 重构解析过程,支持 keypath 映射 196 | 197 | * 2.5.4 支持 Model 转 json;提供 JSON2String 工具方法 198 | 199 | * 2.5.5 修复服务端返回字段是 description,hash 等只读属性字段时的崩溃 200 | 201 | * 2.5.6 解决 Xcode14 找不到 libarclite_iphoneos.a 问题 202 | 203 | * 2.5.7 支持 tvos 平台 204 | -------------------------------------------------------------------------------- /SCJSONUtil/SCModelUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCModelUtil.m 3 | // SCJSONUtil 4 | // 5 | // Created by qianlongxu on 2020/11/15. 6 | // 7 | 8 | #import "SCModelUtil.h" 9 | #import "objc/runtime.h" 10 | #import "scutil.h" 11 | 12 | @implementation NSObject (SCModel2JSON) 13 | 14 | - (NSArray *)sc_propertyNames 15 | { 16 | NSArray *ignoreArr = @[@"superclass", @"description", @"debugDescription", @"hash"]; 17 | 18 | NSArray *(^propertiesForClass)(Class clazz) = ^ NSArray * (Class clazz) { 19 | unsigned int count = 0; 20 | objc_property_t *properties = class_copyPropertyList(clazz, &count); 21 | NSMutableArray *propertyNames = [NSMutableArray array]; 22 | for (int i = 0; i < count; i++) { 23 | NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])]; 24 | if (key) { 25 | if ([ignoreArr containsObject:key]) { 26 | continue; 27 | } 28 | [propertyNames addObject:key]; 29 | } 30 | } 31 | free(properties); 32 | return propertyNames; 33 | }; 34 | 35 | NSMutableArray *properties = [NSMutableArray array]; 36 | Class supclass = [self class]; 37 | do { 38 | NSMutableDictionary *propertyDic = [NSMutableDictionary dictionary]; 39 | [propertyDic setObject:propertiesForClass(supclass) forKey:@"property"]; 40 | [propertyDic setObject:NSStringFromClass(supclass) forKey:@"name"]; 41 | [properties addObject:propertyDic]; 42 | supclass = [supclass superclass]; 43 | } while (supclass != [NSObject class]); 44 | 45 | return [properties copy]; 46 | } 47 | 48 | - (NSString *)sc_typeForProperty:(NSString *)name 49 | { 50 | objc_property_t property = class_getProperty([self class], [name UTF8String]); 51 | if (NULL == property) { 52 | return NULL; 53 | } 54 | // 2.成员类型 55 | const char *encodedType = property_getAttributes(property); 56 | //TB,N,V_boolType 57 | //T@"NSString",C,N,V_stringType 58 | //T@,&,N,V_idType 59 | char *comma = strchr(encodedType, ','); 60 | int bufferLen = (int)(comma - encodedType + 1); 61 | char fullType[bufferLen]; 62 | bzero(fullType, bufferLen); 63 | sscanf(encodedType,"%[^,]",fullType); 64 | 65 | if (strlen(fullType)>=2) { 66 | const char iType = fullType[1]; 67 | switch (iType) { 68 | case '@': 69 | { 70 | //属性是对象类型,这里取出对象的类型,id取不出来; 71 | bool isID = QLCStrEqual("T@", fullType); 72 | if (isID) { 73 | return @"id"; 74 | } else { 75 | char buffer [bufferLen + 1]; 76 | bzero(buffer, bufferLen + 1); 77 | sscanf(fullType, "%*[^\"]\"%[^\"]",buffer); 78 | buffer[strlen(buffer)] = '*'; 79 | return [[NSString alloc]initWithCString:buffer encoding:NSUTF8StringEncoding]; 80 | } 81 | } 82 | break; 83 | case 'f': 84 | { 85 | return @"float"; 86 | } 87 | case 'd': 88 | { 89 | return @"double"; 90 | } 91 | case 'B': 92 | { 93 | return @"BOOL"; 94 | } 95 | case 'c': 96 | { 97 | return @"char"; 98 | } 99 | case 'C': 100 | { 101 | return @"unsigned char"; 102 | } 103 | case 's': 104 | { 105 | return @"short"; 106 | } 107 | case 'S': 108 | { 109 | return @"unsigned short"; 110 | } 111 | case 'i': 112 | { 113 | return @"int"; 114 | } 115 | case 'I': 116 | { 117 | return @"unsigned int"; 118 | } 119 | case 'l': 120 | { 121 | return @"long"; 122 | } 123 | case 'L': 124 | { 125 | return @"unsigned long"; 126 | } 127 | case 'q': 128 | { 129 | return @"long long"; 130 | } 131 | case 'Q': 132 | { 133 | return @"unsigned long long"; 134 | } 135 | default: // #:^ igonre:Class,SEL,Method... 136 | { 137 | return @"unknown"; 138 | } 139 | break; 140 | } 141 | } 142 | return @"unknown"; 143 | } 144 | 145 | - (id)sc_toJSON 146 | { 147 | return [self sc_toJSONWithProperyType:NO]; 148 | } 149 | 150 | - (id)sc_toJSONWithProperyType:(BOOL)printProperyType 151 | { 152 | if ([self isKindOfClass:[NSArray class]]) { 153 | NSMutableArray *json = [NSMutableArray array]; 154 | NSArray *arr = (NSArray *)self; 155 | for (NSObject *obj in arr) { 156 | id o = [obj sc_toJSONWithProperyType:printProperyType]; 157 | if (o) { 158 | [json addObject:o]; 159 | } 160 | } 161 | return [json copy]; 162 | } else if ([self isKindOfClass:[NSDictionary class]]) { 163 | NSMutableDictionary *json = [NSMutableDictionary dictionary]; 164 | NSDictionary *dic = (NSDictionary *)self; 165 | [dic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { 166 | id o = [obj sc_toJSONWithProperyType:printProperyType]; 167 | if (o) { 168 | [json setObject:o forKey:key]; 169 | } 170 | }]; 171 | return [json copy]; 172 | } else if ([self isKindOfClass:[NSNumber class]]) { 173 | return self; 174 | } else if ([self isKindOfClass:[NSNull class]]) { 175 | return [self description]; 176 | } else if ([self isKindOfClass:[NSURL class]]) { 177 | NSURL *url = (NSURL *)self; 178 | return [url absoluteString]; 179 | } else if ([self isKindOfClass:[NSString class]]) { 180 | return [self description]; 181 | } else if ([self isKindOfClass:[NSDate class]]) { 182 | return [self description]; 183 | } else { 184 | NSArray *propertiesForClass = [self sc_propertyNames]; 185 | NSMutableDictionary *json = [NSMutableDictionary dictionary]; 186 | for (NSDictionary *propertyDic in propertiesForClass) { 187 | NSArray *properties = [propertyDic objectForKey:@"property"]; 188 | for (NSString *property in properties) { 189 | id propertyValue = [self valueForKey:property]; 190 | NSString *aKey = property; 191 | if (printProperyType) { 192 | aKey = [NSString stringWithFormat:@"%@ %@",[self sc_typeForProperty:property],property]; 193 | } 194 | id obj = [propertyValue sc_toJSONWithProperyType:printProperyType]; 195 | if (obj) { 196 | [json setObject:obj forKey:aKey]; 197 | } 198 | } 199 | } 200 | return json; 201 | } 202 | } 203 | 204 | @end 205 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/Test.m: -------------------------------------------------------------------------------- 1 | // 2 | // Test.h 3 | // JSONUtilDemo 4 | // 5 | // Created by Reach Matt on 2025/4/1. 6 | // 7 | 8 | #import "Test.h" 9 | #import "Util.h" 10 | #import "UserInfoModel.h" 11 | #import "FavModel.h" 12 | #import "OCTypes.h" 13 | #import "Car.h" 14 | #import "DynamicVideos.h" 15 | #import "VideoInfo.h" 16 | #import "StoreModel.h" 17 | #import 18 | #import 19 | 20 | @implementation NSObject (print) 21 | 22 | - (NSString *)sc_calssName 23 | { 24 | NSString *clazz = NSStringFromClass([self class]); 25 | if ([clazz hasPrefix:@"__"]) { 26 | clazz = NSStringFromClass([self superclass]); 27 | } 28 | if ([clazz isEqualToString:@"NSTaggedPointerString"]) { 29 | clazz = NSStringFromClass([[self superclass]superclass]); 30 | } 31 | return clazz; 32 | } 33 | 34 | - (NSString *)sc_toJSONString:(BOOL)prettyPrinted 35 | { 36 | return [self sc_toJSONString:prettyPrinted printProperyType:NO]; 37 | } 38 | 39 | - (NSString *)sc_toJSONString:(BOOL)prettyPrinted printProperyType:(BOOL)printProperyType 40 | { 41 | NSDictionary *dic = [self sc_toJSONWithProperyType:printProperyType]; 42 | NSString *jsonStr = JSON2String(dic, prettyPrinted); 43 | return [NSString stringWithFormat:@"%@* %p:\n%@",[self sc_calssName],self,jsonStr]; 44 | } 45 | 46 | @end 47 | 48 | @implementation Test 49 | 50 | + (NSString *)testAll 51 | { 52 | SCJSONUtilLog(YES); 53 | 54 | NSString *result = [NSString stringWithFormat:@"\n=======日志开关=======\n%d\n\n",isSCJSONUtilLogOn()]; 55 | 56 | result = [result stringByAppendingString:@"\n=======Objc 基础数据类型解析=======\n\n"]; 57 | 58 | result = [result stringByAppendingString:[self testOCTypes]]; 59 | 60 | result = [result stringByAppendingString:@"\n\n=======通过keypath查找简化解析=======\n\n"]; 61 | 62 | result = [result stringByAppendingString:[self testKeyPathFromDictionary]]; 63 | 64 | result = [result stringByAppendingString:@"\n\n=======json数组转model数组============\n\n"]; 65 | 66 | result = [result stringByAppendingString:[self testModelsFromJSONArr]]; 67 | 68 | result = [result stringByAppendingString:@"\n\n=========指定解析的路径,找到指定 json;==========\n\n"]; 69 | 70 | result = [result stringByAppendingString:[self testKeyPath]]; 71 | 72 | result = [result stringByAppendingString:@"\n\n=========多层嵌套字典转嵌套model==========\n\n"]; 73 | 74 | result = [result stringByAppendingString:[self testModelFromDictionary]]; 75 | 76 | result = [result stringByAppendingString:@"\n\n=========动态参照解析==========\n\n"]; 77 | 78 | result = [result stringByAppendingString:[self testDynamicConvertFromDictionary]]; 79 | 80 | result = [result stringByAppendingString:@"\n\n=========自定义解析==========\n\n"]; 81 | 82 | result = [result stringByAppendingString:[self testCustomConvertFromDictionary]]; 83 | 84 | result = [result stringByAppendingString:@"\n\n===================\n"]; 85 | 86 | return result; 87 | } 88 | 89 | + (NSString *)testOCTypes 90 | { 91 | SCJSONUtilLog(YES); 92 | 93 | NSDictionary *typesDic = [Util readOCTypes]; 94 | OCTypes *model = [OCTypes sc_instanceFormDic:typesDic]; 95 | //美化打印 96 | return [model sc_toJSONString:YES]; 97 | } 98 | 99 | + (NSString *)testKeyPathFromDictionary 100 | { 101 | SCJSONUtilLog(YES); 102 | 103 | NSDictionary *json = [Util readStore]; 104 | StoreModel *store = SCJSON2Model(json, @"StoreModel"); 105 | //美化打印,并且json的key值里包含定义属性的类型 106 | return [store sc_toJSONString:YES printProperyType:YES]; 107 | } 108 | 109 | + (void)testCount:(long)count work:(dispatch_block_t)block 110 | { 111 | if (!block) { 112 | return; 113 | } 114 | CFAbsoluteTime begin = CFAbsoluteTimeGetCurrent(); 115 | for (int i = 0; i 10 | #import 11 | #import 12 | #import "UserInfoModel.h" 13 | #import "FavModel.h" 14 | #import "OCTypes.h" 15 | #import "Car.h" 16 | #import "DynamicVideos.h" 17 | #import "VideoInfo.h" 18 | #import "GalleryModel.h" 19 | #import "StoreModel.h" 20 | #import "Util.h" 21 | 22 | @interface SCJSONUtilTests : XCTestCase 23 | 24 | @end 25 | 26 | @implementation SCJSONUtilTests 27 | 28 | - (void)setUp { 29 | // Put setup code here. This method is called before the invocation of each test method in the class. 30 | } 31 | 32 | - (void)tearDown { 33 | // Put teardown code here. This method is called after the invocation of each test method in the class. 34 | } 35 | 36 | - (void)testLogSwitch 37 | { 38 | SCJSONUtilLog(YES); 39 | XCTAssert(isSCJSONUtilLogOn() == YES); 40 | SCJSONUtilLog(NO); 41 | XCTAssert(isSCJSONUtilLogOn() == NO); 42 | } 43 | 44 | - (void)rawJson:(id)json model:(id)model 45 | { 46 | //测试下json转成的model,再转回json跟原json是否相等; 47 | NSDictionary *json2 = [model sc_toJSON]; 48 | //以model转成的josn为主,因为可能有的model没有解析json的所有字段 49 | [json2 enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { 50 | id obj2 = json[key]; 51 | NSLog(@"test equal[%@:%@:%@]",key,obj,obj2); 52 | //由于浮点数字不能直接比较,因此这里统一转为字符串再比 53 | XCTAssert([[obj2 description] isEqual:[obj description]]); 54 | }]; 55 | } 56 | 57 | - (void)testOCTypes 58 | { 59 | NSDictionary *typesDic = [Util readOCTypes]; 60 | OCTypes *model = [OCTypes sc_instanceFormDic:typesDic]; 61 | [self rawJson:typesDic model:model]; 62 | 63 | XCTAssert(model.boolType == YES); 64 | XCTAssert(model.BOOLType == YES); 65 | XCTAssert(model.charType == 32); 66 | XCTAssert(model.uCharType == 97); 67 | XCTAssert(model.shortType == 123); 68 | XCTAssert(model.uShortType == 234); 69 | XCTAssert(model.intType == 345); 70 | XCTAssert(model.uIntType == 456); 71 | XCTAssert(model.longType == 567); 72 | XCTAssert(model.uLongType == 789); 73 | XCTAssert(model.longlongType == 5677); 74 | XCTAssert(model.uLongLongType == 7899); 75 | XCTAssert((int)(model.floatType * 1000) == (int)(345.123 * 1000)); 76 | XCTAssert((long)(model.doubleType * 100000) == (long)(456.12345 * 100000)); 77 | XCTAssert([model.stringType isEqualToString:@"I'm a string."]); 78 | XCTAssert([model.mutableStringType isEqualToString:@"I'm a mutable string."]); 79 | XCTAssert([model.mutableStringType isKindOfClass:[NSMutableString class]]); 80 | XCTAssert([[model.numberType description] isEqualToString:@"7982"]); 81 | XCTAssert([model.numberType isKindOfClass:[NSNumber class]]); 82 | XCTAssert([[model.urlType absoluteString] isEqualToString:@"https://debugly.cn"]); 83 | XCTAssert([model.urlType isKindOfClass:[NSURL class]]); 84 | XCTAssert([[model.fileURLType absoluteString] isEqualToString:@"file:///Users/qianlongxu/Desktop/Caption.zip"]); 85 | XCTAssert([model.fileURLType isFileURL]); 86 | XCTAssert([model.fileURLType isKindOfClass:[NSURL class]]); 87 | XCTAssert([model.classAType isKindOfClass:[OCClassA class]]); 88 | XCTAssert([model.classAType.name isEqualToString:@"I'm a classA instance."]); 89 | XCTAssert([model.idType isEqualToString:@"I'm id type."]); 90 | XCTAssert(model.skipMe == nil); 91 | 92 | } 93 | 94 | - (void)performance:(long)count work:(dispatch_block_t)block 95 | { 96 | [self measureBlock:^{ 97 | for (int i = 0; i 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Apache License 18 | Version 2.0, January 2004 19 | http://www.apache.org/licenses/ 20 | 21 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 22 | 23 | 1. Definitions. 24 | 25 | "License" shall mean the terms and conditions for use, reproduction, 26 | and distribution as defined by Sections 1 through 9 of this document. 27 | 28 | "Licensor" shall mean the copyright owner or entity authorized by 29 | the copyright owner that is granting the License. 30 | 31 | "Legal Entity" shall mean the union of the acting entity and all 32 | other entities that control, are controlled by, or are under common 33 | control with that entity. For the purposes of this definition, 34 | "control" means (i) the power, direct or indirect, to cause the 35 | direction or management of such entity, whether by contract or 36 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 37 | outstanding shares, or (iii) beneficial ownership of such entity. 38 | 39 | "You" (or "Your") shall mean an individual or Legal Entity 40 | exercising permissions granted by this License. 41 | 42 | "Source" form shall mean the preferred form for making modifications, 43 | including but not limited to software source code, documentation 44 | source, and configuration files. 45 | 46 | "Object" form shall mean any form resulting from mechanical 47 | transformation or translation of a Source form, including but 48 | not limited to compiled object code, generated documentation, 49 | and conversions to other media types. 50 | 51 | "Work" shall mean the work of authorship, whether in Source or 52 | Object form, made available under the License, as indicated by a 53 | copyright notice that is included in or attached to the work 54 | (an example is provided in the Appendix below). 55 | 56 | "Derivative Works" shall mean any work, whether in Source or Object 57 | form, that is based on (or derived from) the Work and for which the 58 | editorial revisions, annotations, elaborations, or other modifications 59 | represent, as a whole, an original work of authorship. For the purposes 60 | of this License, Derivative Works shall not include works that remain 61 | separable from, or merely link (or bind by name) to the interfaces of, 62 | the Work and Derivative Works thereof. 63 | 64 | "Contribution" shall mean any work of authorship, including 65 | the original version of the Work and any modifications or additions 66 | to that Work or Derivative Works thereof, that is intentionally 67 | submitted to Licensor for inclusion in the Work by the copyright owner 68 | or by an individual or Legal Entity authorized to submit on behalf of 69 | the copyright owner. For the purposes of this definition, "submitted" 70 | means any form of electronic, verbal, or written communication sent 71 | to the Licensor or its representatives, including but not limited to 72 | communication on electronic mailing lists, source code control systems, 73 | and issue tracking systems that are managed by, or on behalf of, the 74 | Licensor for the purpose of discussing and improving the Work, but 75 | excluding communication that is conspicuously marked or otherwise 76 | designated in writing by the copyright owner as "Not a Contribution." 77 | 78 | "Contributor" shall mean Licensor and any individual or Legal Entity 79 | on behalf of whom a Contribution has been received by Licensor and 80 | subsequently incorporated within the Work. 81 | 82 | 2. Grant of Copyright License. Subject to the terms and conditions of 83 | this License, each Contributor hereby grants to You a perpetual, 84 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 85 | copyright license to reproduce, prepare Derivative Works of, 86 | publicly display, publicly perform, sublicense, and distribute the 87 | Work and such Derivative Works in Source or Object form. 88 | 89 | 3. Grant of Patent License. Subject to the terms and conditions of 90 | this License, each Contributor hereby grants to You a perpetual, 91 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 92 | (except as stated in this section) patent license to make, have made, 93 | use, offer to sell, sell, import, and otherwise transfer the Work, 94 | where such license applies only to those patent claims licensable 95 | by such Contributor that are necessarily infringed by their 96 | Contribution(s) alone or by combination of their Contribution(s) 97 | with the Work to which such Contribution(s) was submitted. If You 98 | institute patent litigation against any entity (including a 99 | cross-claim or counterclaim in a lawsuit) alleging that the Work 100 | or a Contribution incorporated within the Work constitutes direct 101 | or contributory patent infringement, then any patent licenses 102 | granted to You under this License for that Work shall terminate 103 | as of the date such litigation is filed. 104 | 105 | 4. Redistribution. You may reproduce and distribute copies of the 106 | Work or Derivative Works thereof in any medium, with or without 107 | modifications, and in Source or Object form, provided that You 108 | meet the following conditions: 109 | 110 | (a) You must give any other recipients of the Work or 111 | Derivative Works a copy of this License; and 112 | 113 | (b) You must cause any modified files to carry prominent notices 114 | stating that You changed the files; and 115 | 116 | (c) You must retain, in the Source form of any Derivative Works 117 | that You distribute, all copyright, patent, trademark, and 118 | attribution notices from the Source form of the Work, 119 | excluding those notices that do not pertain to any part of 120 | the Derivative Works; and 121 | 122 | (d) If the Work includes a "NOTICE" text file as part of its 123 | distribution, then any Derivative Works that You distribute must 124 | include a readable copy of the attribution notices contained 125 | within such NOTICE file, excluding those notices that do not 126 | pertain to any part of the Derivative Works, in at least one 127 | of the following places: within a NOTICE text file distributed 128 | as part of the Derivative Works; within the Source form or 129 | documentation, if provided along with the Derivative Works; or, 130 | within a display generated by the Derivative Works, if and 131 | wherever such third-party notices normally appear. The contents 132 | of the NOTICE file are for informational purposes only and 133 | do not modify the License. You may add Your own attribution 134 | notices within Derivative Works that You distribute, alongside 135 | or as an addendum to the NOTICE text from the Work, provided 136 | that such additional attribution notices cannot be construed 137 | as modifying the License. 138 | 139 | You may add Your own copyright statement to Your modifications and 140 | may provide additional or different license terms and conditions 141 | for use, reproduction, or distribution of Your modifications, or 142 | for any such Derivative Works as a whole, provided Your use, 143 | reproduction, and distribution of the Work otherwise complies with 144 | the conditions stated in this License. 145 | 146 | 5. Submission of Contributions. Unless You explicitly state otherwise, 147 | any Contribution intentionally submitted for inclusion in the Work 148 | by You to the Licensor shall be under the terms and conditions of 149 | this License, without any additional terms or conditions. 150 | Notwithstanding the above, nothing herein shall supersede or modify 151 | the terms of any separate license agreement you may have executed 152 | with Licensor regarding such Contributions. 153 | 154 | 6. Trademarks. This License does not grant permission to use the trade 155 | names, trademarks, service marks, or product names of the Licensor, 156 | except as required for reasonable and customary use in describing the 157 | origin of the Work and reproducing the content of the NOTICE file. 158 | 159 | 7. Disclaimer of Warranty. Unless required by applicable law or 160 | agreed to in writing, Licensor provides the Work (and each 161 | Contributor provides its Contributions) on an "AS IS" BASIS, 162 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 163 | implied, including, without limitation, any warranties or conditions 164 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 165 | PARTICULAR PURPOSE. You are solely responsible for determining the 166 | appropriateness of using or redistributing the Work and assume any 167 | risks associated with Your exercise of permissions under this License. 168 | 169 | 8. Limitation of Liability. In no event and under no legal theory, 170 | whether in tort (including negligence), contract, or otherwise, 171 | unless required by applicable law (such as deliberate and grossly 172 | negligent acts) or agreed to in writing, shall any Contributor be 173 | liable to You for damages, including any direct, indirect, special, 174 | incidental, or consequential damages of any character arising as a 175 | result of this License or out of the use or inability to use the 176 | Work (including but not limited to damages for loss of goodwill, 177 | work stoppage, computer failure or malfunction, or any and all 178 | other commercial damages or losses), even if such Contributor 179 | has been advised of the possibility of such damages. 180 | 181 | 9. Accepting Warranty or Additional Liability. While redistributing 182 | the Work or Derivative Works thereof, You may choose to offer, 183 | and charge a fee for, acceptance of support, warranty, indemnity, 184 | or other liability obligations and/or rights consistent with this 185 | License. However, in accepting such obligations, You may act only 186 | on Your own behalf and on Your sole responsibility, not on behalf 187 | of any other Contributor, and only if You agree to indemnify, 188 | defend, and hold each Contributor harmless for any liability 189 | incurred by, or claims asserted against, such Contributor by reason 190 | of your accepting any such warranty or additional liability. 191 | 192 | END OF TERMS AND CONDITIONS 193 | 194 | APPENDIX: How to apply the Apache License to your work. 195 | 196 | To apply the Apache License to your work, attach the following 197 | boilerplate notice, with the fields enclosed by brackets "{}" 198 | replaced with your own identifying information. (Don't include 199 | the brackets!) The text should be enclosed in the appropriate 200 | comment syntax for the file format. We also recommend that a 201 | file or class name and description of purpose be included on the 202 | same "printed page" as the copyright notice for easier 203 | identification within third-party archives. 204 | 205 | Copyright {yyyy} {name of copyright owner} 206 | 207 | Licensed under the Apache License, Version 2.0 (the "License"); 208 | you may not use this file except in compliance with the License. 209 | You may obtain a copy of the License at 210 | 211 | http://www.apache.org/licenses/LICENSE-2.0 212 | 213 | Unless required by applicable law or agreed to in writing, software 214 | distributed under the License is distributed on an "AS IS" BASIS, 215 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 216 | See the License for the specific language governing permissions and 217 | limitations under the License. 218 | 219 | 220 | License 221 | Apache License 222 | Title 223 | SCJSONUtil 224 | Type 225 | PSGroupSpecifier 226 | 227 | 228 | FooterText 229 | Generated by CocoaPods - https://cocoapods.org 230 | Title 231 | 232 | Type 233 | PSGroupSpecifier 234 | 235 | 236 | StringsTable 237 | Acknowledgements 238 | Title 239 | Acknowledgements 240 | 241 | 242 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/jsonfiles/GalleryList.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": "0", 3 | "content": { 4 | "gallery": [ 5 | { 6 | "isFlagship": "0", 7 | "name": "白色情人节 与浪漫牵手", 8 | "pic": "http://pic16.shangpin.com/e/s/15/03/06/20150306174649601525-10-10.jpg", 9 | "refContent": "http://m.shangpin.com/meet/189", 10 | "type": "1" 11 | }, 12 | { 13 | "isFlagship": "1", 14 | "name": "【早春新品预售】", 15 | "pic": "http://pic11.shangpin.com/e/s/15/03/13/20150313101837452024-10-10.jpg", 16 | "refContent": "50310992", 17 | "type": "2" 18 | }, 19 | { 20 | "isFlagship": "0", 21 | "name": "【男装春季促销】", 22 | "pic": "http://pic12.shangpin.com/e/s/15/03/12/20150312092557150616-10-10.jpg", 23 | "refContent": "50310978", 24 | "type": "3" 25 | }, 26 | { 27 | "isFlagship": "1", 28 | "name": "【男装春季促销】", 29 | "pic": "http://pic12.shangpin.com/e/s/15/03/12/20150312092557150616-10-10.jpg", 30 | "refContent": "50310978", 31 | "type": "4" 32 | }, 33 | { 34 | "isFlagship": "0", 35 | "name": "【男装春季促销】", 36 | "pic": "http://pic12.shangpin.com/e/s/15/03/12/20150312092557150616-10-10.jpg", 37 | "refContent": "50310978", 38 | "type": "5" 39 | }, 40 | { 41 | "isFlagship": "1", 42 | "name": "【男装春季促销】", 43 | "pic": "http://pic12.shangpin.com/e/s/15/03/12/20150312092557150616-10-10.jpg", 44 | "refContent": "50310978", 45 | "type": "6" 46 | } 47 | ], 48 | "entrance": [ 49 | { 50 | "isFlagship": "0", 51 | "name": "时尚潮时尚", 52 | "pic": "http://pic12.shangpin.com/e/s/15/03/03/20150303151320537363-10-10.jpg", 53 | "refContent": "http://m.shangpin.com/meet/185", 54 | "type": "5" 55 | }, 56 | { 57 | "isFlagship": "0", 58 | "name": "优惠券留", 59 | "pic": "http://pic14.shangpin.com/e/s/15/03/05/20150305164615551722-10-10.jpg", 60 | "refContent": "50305792", 61 | "type": "1" 62 | }, 63 | { 64 | "isFlagship": "0", 65 | "name": "物流查", 66 | "pic": "http://pic15.shangpin.com/e/s/15/03/03/20150303155535618638-10-10.jpg", 67 | "refContent": "http://m.shangpin.com/meet/188", 68 | "type": "5" 69 | }, 70 | { 71 | "isFlagship": "0", 72 | "name": "入口", 73 | "pic": "http://pic14.shangpin.com/e/s/15/03/05/20150305164615551722-10-10.jpg", 74 | "refContent": "50305792", 75 | "type": "1" 76 | }, 77 | { 78 | "isFlagship": "0", 79 | "name": "礼品卡", 80 | "pic": "http://pic15.shangpin.com/e/s/15/03/03/20150303155535618638-10-10.jpg", 81 | "refContent": "http://m.shangpin.com/meet/188", 82 | "type": "5" 83 | }, 84 | { 85 | "name": "不错哦", 86 | "pic": "http://pic14.shangpin.com/e/s/15/03/05/20150305142326260964-10-10.jpg", 87 | "refContent": "50302515", 88 | "type": "1" 89 | }, 90 | { 91 | "name": "购物车", 92 | "pic": "http://pic12.shangpin.com/e/s/15/03/05/20150305143006228421-10-10.jpg", 93 | "refContent": "50215339", 94 | "type": "1" 95 | }, 96 | { 97 | "name": "列表页", 98 | "pic": "http://pic14.shangpin.com/e/s/15/03/05/20150305142326260964-10-10.jpg", 99 | "refContent": "50302515", 100 | "type": "1" 101 | } 102 | ], 103 | "releases": [ 104 | { 105 | "desc": "", 106 | "endTime": "1426554000000", 107 | "height": "720", 108 | "name": "【TOPSHOP 2000条牛仔裤补货到】", 109 | "pic": "http://pic12.shangpin.com/e/s/15/03/12/20150312132707142163-10-10.jpg", 110 | "refContent": "50311019", 111 | "startTag": "03.13", 112 | "startTime": "1426212600000", 113 | "type": "1", 114 | "width": "720" 115 | }, 116 | { 117 | "desc": "MO&CO.新品特惠", 118 | "endTime": "1426467600000", 119 | "height": "490", 120 | "name": "MO&CO.新品特惠", 121 | "pic": "http://pic14.shangpin.com/e/s/15/03/13/20150313095913400551-10-10.jpg", 122 | "refContent": "50306843", 123 | "startTag": "03.13", 124 | "startTime": "1426209300000", 125 | "type": "1", 126 | "width": "720" 127 | }, 128 | { 129 | "desc": "美式轻奢风", 130 | "endTime": "1426467600000", 131 | "height": "580", 132 | "name": "【新品预售 3月18日发货】", 133 | "pic": "http://pic11.shangpin.com/e/s/15/03/09/20150309145151259290-10-10.jpg", 134 | "refContent": "50309891", 135 | "startTag": "03.13", 136 | "startTime": "1426208400000", 137 | "type": "1", 138 | "width": "720" 139 | } 140 | ], 141 | "sale": [ 142 | { 143 | "list": [ 144 | { 145 | "desc": "新品网络独家首发", 146 | "endTime": "1426467600000", 147 | "height": "670", 148 | "name": "【STELLA LUNA新品网络首发】", 149 | "pic": "http://pic16.shangpin.com/e/s/15/03/06/20150306170847472468-10-10.jpg", 150 | "refContent": "50306871", 151 | "startTag": "09:00", 152 | "startTime": "1426208400000", 153 | "type": "1", 154 | "width": "720" 155 | }, 156 | { 157 | "desc": "意大利新品睡衣", 158 | "endTime": "1426813200000", 159 | "height": "550", 160 | "name": "意大利进口 新品睡衣", 161 | "pic": "http://pic16.shangpin.com/e/s/15/03/09/20150309105605840242-10-10.jpg", 162 | "refContent": "50302526", 163 | "startTag": "09:00", 164 | "startTime": "1426208400000", 165 | "type": "1", 166 | "width": "720" 167 | }, 168 | { 169 | "desc": "打底裤集合", 170 | "endTime": "1426467600000", 171 | "height": "350", 172 | "name": "打底裤集合", 173 | "pic": "http://pic14.shangpin.com/e/s/15/03/09/20150309153725584647-10-10.jpg", 174 | "refContent": "50303576", 175 | "startTag": "09:00", 176 | "startTime": "1426208400000", 177 | "type": "1", 178 | "width": "720" 179 | }, 180 | { 181 | "desc": "早春美饰上新", 182 | "endTime": "1426467600000", 183 | "height": "1050", 184 | "name": "炫彩水晶 低调华丽", 185 | "pic": "http://pic14.shangpin.com/e/s/15/03/10/20150310161702642930-10-10.jpg", 186 | "refContent": "50304615", 187 | "startTag": "09:00", 188 | "startTime": "1426208400000", 189 | "type": "1", 190 | "width": "720" 191 | }, 192 | { 193 | "desc": "阿玛尼新年特惠", 194 | "endTime": "1426467600000", 195 | "height": "50", 196 | "name": "【阿玛尼新年特惠】 ", 197 | "pic": "http://pic15.shangpin.com/e/s/15/03/12/20150312142428193691-10-10.jpg", 198 | "refContent": "50304727", 199 | "startTag": "09:00", 200 | "startTime": "1426208400000", 201 | "type": "1", 202 | "width": "720" 203 | }, 204 | { 205 | "desc": "", 206 | "endTime": "1428135660000", 207 | "height": "150", 208 | "name": "kate spade", 209 | "pic": "http://pic15.shangpin.com/e/s/15/03/12/20150312162128286598-10-10.jpg", 210 | "refContent": "50312101", 211 | "startTag": "09:00", 212 | "startTime": "1426208400000", 213 | "type": "1", 214 | "width": "720" 215 | } 216 | ], 217 | "title": "今日特卖 DAILY EVENTS", 218 | "type": "1" 219 | }, 220 | { 221 | "list": [ 222 | { 223 | "desc": "波西米亚风女装", 224 | "endTime": "1426485600000", 225 | "height": "250", 226 | "name": "波西米亚风女装", 227 | "pic": "http://pic14.shangpin.com/e/s/15/03/09/20150309153809514337-10-10.jpg", 228 | "refContent": "50304587", 229 | "startTag": "14:00", 230 | "startTime": "1426226400000", 231 | "type": "1", 232 | "width": "720" 233 | }, 234 | { 235 | "desc": "高级彩宝专场", 236 | "endTime": "1426485600000", 237 | "height": "850", 238 | "name": "时尚彩宝 高贵专属", 239 | "pic": "http://pic11.shangpin.com/e/s/15/03/10/20150310162008391046-10-10.jpg", 240 | "refContent": "50304625", 241 | "startTag": "14:00", 242 | "startTime": "1426226400000", 243 | "type": "1", 244 | "width": "720" 245 | }, 246 | { 247 | "desc": "", 248 | "endTime": "1428135780000", 249 | "height": "650", 250 | "name": "saint laurent", 251 | "pic": "http://pic11.shangpin.com/e/s/15/03/12/20150312162313055123-10-10.jpg", 252 | "refContent": "50312103", 253 | "startTag": "14:00", 254 | "startTime": "1426226400000", 255 | "type": "1", 256 | "width": "720" 257 | } 258 | ], 259 | "title": "即将开始 (每日14:00开启)", 260 | "type": "2" 261 | } 262 | ], 263 | "sysTime": "1426226372902", 264 | "themes": [ 265 | { 266 | "height": "550", 267 | "name": "Salvatore Ferragamo", 268 | "pic": "http://pic14.shangpin.com/e/s/15/03/09/20150309113148373426-10-10.jpg", 269 | "refContent": "50305771", 270 | "type": "1", 271 | "width": "720" 272 | }, 273 | { 274 | "height": "450", 275 | "name": "【早春新品预售】", 276 | "pic": "http://pic13.shangpin.com/e/s/15/03/13/20150313101818280750-10-10.jpg", 277 | "refContent": "50310992", 278 | "type": "1", 279 | "width": "720" 280 | }, 281 | { 282 | "height": "800", 283 | "name": "【TOPSHOP 上班女郎穿衣术】", 284 | "pic": "http://pic11.shangpin.com/e/s/15/03/12/20150312170612069350-10-10.jpg", 285 | "refContent": "50303556", 286 | "type": "1", 287 | "width": "720" 288 | } 289 | ], 290 | "brand": [ 291 | { 292 | "refContent": "111111111", 293 | "nameEN": "TopShop", 294 | "nameCN": "TopShop", 295 | "desc": "描述摘要", 296 | "pic": "http://pic16.shangpin.com/e/s/15/02/15/20150215155540295340-10-10.jpg", 297 | "type": "3", 298 | "refContent": "" 299 | },{ 300 | "refContent": "2222222", 301 | "nameEN": "TopShop", 302 | "nameCN": "TopShop", 303 | "desc": "描述摘要", 304 | "pic": "http://pic12.shangpin.com/e/s/15/02/15/20150215154810871864-10-10.jpg", 305 | "type": "3", 306 | "refContent": "" 307 | },{ 308 | "refContent": "3333333", 309 | "nameEN": "TopShop", 310 | "nameCN": "TopShop", 311 | "desc": "描述摘要", 312 | "pic": "http://pic14.shangpin.com/e/s/15/04/01/20150401185027193594-10-10.jpg", 313 | "type": "3", 314 | "refContent": "" 315 | },{ 316 | "refContent": "4444444", 317 | "nameEN": "TopShop", 318 | "nameCN": "TopShop", 319 | "desc": "描述摘要", 320 | "pic": "http://pic11.shangpin.com/e/s/15/02/15/20150215150055287171-10-10.jpg", 321 | "type": "3", 322 | "refContent": "" 323 | },{ 324 | "refContent": "5555555", 325 | "nameEN": "TopShop", 326 | "nameCN": "TopShop", 327 | "desc": "描述摘要", 328 | "pic": "http://pic14.shangpin.com/e/s/15/02/15/20150215155043347252-10-10.jpg", 329 | "type": "3", 330 | "refContent": "" 331 | },{ 332 | "refContent": "6666666", 333 | "nameEN": "TopShop", 334 | "nameCN": "TopShop", 335 | "desc": "描述摘要", 336 | "pic": "http://pic12.shangpin.com/e/s/15/02/15/20150215150258387585-10-10.jpg", 337 | "type": "3", 338 | "refContent": "" 339 | },{ 340 | "refContent": "7777777", 341 | "nameEN": "TopShop", 342 | "nameCN": "TopShop", 343 | "desc": "描述摘要", 344 | "pic": "http://pic13.shangpin.com/e/s/15/02/15/20150215155140474254-10-10.jpg", 345 | "type": "3", 346 | "refContent": "" 347 | },{ 348 | "refContent": "8888888", 349 | "nameEN": "TopShop", 350 | "nameCN": "TopShop", 351 | "desc": "描述摘要", 352 | "pic": "http://pic14.shangpin.com/e/s/15/02/15/20150215154845410359-10-10.jpg", 353 | "type": "3", 354 | "refContent": "" 355 | },{ 356 | "refContent": "9999999", 357 | "nameEN": "TopShop", 358 | "nameCN": "TopShop", 359 | "desc": "描述摘要", 360 | "pic": "http://pic16.shangpin.com/e/s/15/02/15/20150215151504769908-10-10.jpg", 361 | "type": "3", 362 | "refContent": "" 363 | },{ 364 | "refContent": "1000000", 365 | "nameEN": "TopShop", 366 | "nameCN": "TopShop", 367 | "desc": "描述摘要", 368 | "pic": "http://pic15.shangpin.com/e/s/15/04/13/20150413174239555535-10-10.jpg", 369 | "type": "3", 370 | "refContent": "" 371 | },{ 372 | "refContent": "1100000", 373 | "nameEN": "TopShop", 374 | "nameCN": "TopShop", 375 | "desc": "描述摘要", 376 | "pic": "http://pic15.shangpin.com/e/s/15/03/27/20150327170622288008-10-10.jpg", 377 | "type": "3", 378 | "refContent": "" 379 | },{ 380 | "refContent": "1200000", 381 | "nameEN": "TopShop", 382 | "nameCN": "TopShop", 383 | "desc": "描述摘要", 384 | "pic": "http://pic12.shangpin.com/e/s/15/02/15/20150215164135101731-10-10.jpg", 385 | "type": "3", 386 | "refContent": "" 387 | },{ 388 | "refContent": "1300000", 389 | "nameEN": "TopShop", 390 | "nameCN": "TopShop", 391 | "desc": "描述摘要", 392 | "pic": "http://pic15.shangpin.com/e/s/15/02/15/20150215154213693963-10-10.jpg", 393 | "type": "3", 394 | "refContent": "" 395 | },{ 396 | "refContent": "1400000", 397 | "nameEN": "TopShop", 398 | "nameCN": "TopShop", 399 | "desc": "描述摘要", 400 | "pic": "http://pic15.shangpin.com/e/s/15/02/15/20150215154009066205-10-10.jpg", 401 | "type": "3", 402 | "refContent": "" 403 | },{ 404 | "refContent": "1500000", 405 | "nameEN": "TopShop", 406 | "nameCN": "TopShop", 407 | "desc": "描述摘要", 408 | "pic": "http://pic15.shangpin.com/e/s/15/04/01/20150401153506809822-10-10.jpg", 409 | "type": "3", 410 | "refContent": "" 411 | },{ 412 | "refContent": "1600000", 413 | "nameEN": "TopShop", 414 | "nameCN": "TopShop", 415 | "desc": "描述摘要", 416 | "pic": "http://pic15.shangpin.com/e/s/15/02/15/20150215154757876484-10-10.jpg", 417 | "type": "3", 418 | "refContent": "" 419 | },{ 420 | "refContent": "1600000", 421 | "nameEN": "TopShop", 422 | "nameCN": "TopShop", 423 | "desc": "描述摘要", 424 | "pic": "http://pic16.shangpin.com/e/s/15/02/15/20150215155121052546-10-10.jpg", 425 | "type": "3", 426 | "refContent": "" 427 | },{ 428 | "refContent": "1600000", 429 | "nameEN": "TopShop", 430 | "nameCN": "TopShop", 431 | "desc": "描述摘要", 432 | "pic": "http://pic14.shangpin.com/e/s/15/02/15/20150215155558703893-10-10.jpg", 433 | "type": "3", 434 | "refContent": "" 435 | },{ 436 | "refContent": "1600000", 437 | "nameEN": "TopShop", 438 | "nameCN": "TopShop", 439 | "desc": "描述摘要", 440 | "pic": "http://pic11.shangpin.com/e/s/15/03/27/20150327170606111386-10-10.jpg", 441 | "type": "3", 442 | "refContent": "" 443 | 444 | } 445 | ], 446 | "fashion": [ 447 | { 448 | "name": "尚潮流标题", 449 | "type": "5", 450 | "pubTime": "83838928291", 451 | "pic": "http://pic13.shangpin.com/e/s/15/03/13/20150313101818280750-10-10.jpg", 452 | "summary": "文章摘要", 453 | "source": "文章来源", 454 | "refContent": "http://m.shangpin.com/meet/185" 455 | },{ 456 | "name": "尚潮流标题", 457 | "type": "5", 458 | "pubTime": "83838928291", 459 | "pic": "http://pic13.shangpin.com/e/s/15/03/13/20150313101818280750-10-10.jpg", 460 | "summary": "文章摘要", 461 | "source": "文章来源", 462 | "refContent": "http://m.shangpin.com/meet/185" 463 | },{ 464 | "name": "尚潮流标题", 465 | "type": "5", 466 | "pubTime": "83838928291", 467 | "pic": "http://pic13.shangpin.com/e/s/15/03/13/20150313101818280750-10-10.jpg", 468 | "summary": "文章摘要", 469 | "source": "文章来源", 470 | "refContent": "http://m.shangpin.com/meet/185" 471 | },{ 472 | "name": "尚潮流标题", 473 | "type": "5", 474 | "pubTime": "83838928291", 475 | "pic": "http://pic13.shangpin.com/e/s/15/03/13/20150313101818280750-10-10.jpg", 476 | "summary": "文章摘要", 477 | "source": "文章来源", 478 | "refContent": "http://m.shangpin.com/meet/185" 479 | },{ 480 | "name": "尚潮流标题", 481 | "type": "5", 482 | "pubTime": "83838928291", 483 | "pic": "http://pic13.shangpin.com/e/s/15/03/13/20150313101818280750-10-10.jpg", 484 | "summary": "文章摘要", 485 | "source": "文章来源", 486 | "refContent": "http://m.shangpin.com/meet/185" 487 | },{ 488 | "name": "尚潮流标题", 489 | "type": "5", 490 | "pubTime": "83838928291", 491 | "pic": "http://pic13.shangpin.com/e/s/15/03/13/20150313101818280750-10-10.jpg", 492 | "summary": "文章摘要", 493 | "source": "文章来源", 494 | "refContent": "http://m.shangpin.com/meet/185" 495 | },{ 496 | "name": "尚潮流标题", 497 | "type": "5", 498 | "pubTime": "83838928291", 499 | "pic": "http://pic13.shangpin.com/e/s/15/03/13/20150313101818280750-10-10.jpg", 500 | "summary": "文章摘要", 501 | "source": "文章来源", 502 | "refContent": "http://m.shangpin.com/meet/185" 503 | } 504 | ] 505 | }, 506 | "msg": "" 507 | } 508 | -------------------------------------------------------------------------------- /Demo/SCJSONUtilDemo/jsonfiles/NewRecProduct.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": "0", 3 | "msg": "成功", 4 | "content": { 5 | "systime": "12312134212", 6 | "recommendNum": "为该用户推荐的单品量", 7 | "list": [ 8 | { 9 | "productId": "1001", 10 | "productName": "碎花连衣裙", 11 | "advertWord": "有广告语的时候优先选择使用广告语,", 12 | "brandNameCN": "菲拉格慕", 13 | "brandNameEN": "SALVATORE FERRAGAMO", 14 | "brandNo": "B0084", 15 | "marketPrice": "1899", 16 | "limitedPrice": "1899", 17 | "goldPrice": "1899", 18 | "platinumPrice": "1899", 19 | "diamondPrice": "1899", 20 | "limitedVipPrice": "899", 21 | "promotionPrice": "699", 22 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 23 | "promotionDesc": "促销描述", 24 | "count": "2", 25 | "collections": "2222", 26 | "comments": "1111", 27 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 28 | "status": "0" 29 | },{ 30 | "productId": "1001", 31 | "productName": "碎花连衣裙", 32 | "advertWord": "臣本布衣,躬耕于南阳,苟全性出师于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间:尔来二十有一年矣。", 33 | "brandNameCN": "菲拉格慕", 34 | "brandNameEN": "SALVATORE FERRAGAMO", 35 | "brandNo": "B0084", 36 | "marketPrice": "1899", 37 | "limitedPrice": "1899", 38 | "goldPrice": "1899", 39 | "platinumPrice": "1899", 40 | "diamondPrice": "1899", 41 | "limitedVipPrice": "899", 42 | "promotionPrice": "699", 43 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 44 | "promotionDesc": "促销描述", 45 | "count": "2", 46 | "collections": "1111", 47 | "comments": "1111", 48 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 49 | "status": "0" 50 | },{ 51 | "productId": "1001", 52 | "productName": "碎花连衣裙", 53 | "advertWord": "", 54 | "brandNameCN": "菲拉格慕", 55 | "brandNameEN": "SALVATORE FERRAGAMO", 56 | "brandNo": "B0084", 57 | "marketPrice": "1899", 58 | "limitedPrice": "1899", 59 | "goldPrice": "1899", 60 | "platinumPrice": "1899", 61 | "diamondPrice": "1899", 62 | "limitedVipPrice": "899", 63 | "promotionPrice": "699", 64 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 65 | "promotionDesc": "促销描述", 66 | "count": "2", 67 | "collections": "2222", 68 | "comments": "1111", 69 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 70 | "status": "0" 71 | },{ 72 | "productId": "1001", 73 | "productName": "碎花连衣裙", 74 | "advertWord": "", 75 | "brandNameCN": "菲拉格慕", 76 | "brandNameEN": "SALVATORE FERRAGAMO", 77 | "brandNo": "B0084", 78 | "marketPrice": "1899", 79 | "limitedPrice": "1899", 80 | "goldPrice": "1899", 81 | "platinumPrice": "1899", 82 | "diamondPrice": "1899", 83 | "limitedVipPrice": "899", 84 | "promotionPrice": "699", 85 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 86 | "promotionDesc": "促销描述", 87 | "count": "2", 88 | "collections": "1111", 89 | "comments": "1111", 90 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 91 | "status": "0" 92 | },{ 93 | "productId": "1001", 94 | "productName": "碎花连衣裙", 95 | "advertWord": "", 96 | "brandNameCN": "菲拉格慕", 97 | "brandNameEN": "SALVATORE FERRAGAMO", 98 | "brandNo": "B0084", 99 | "marketPrice": "1899", 100 | "limitedPrice": "1899", 101 | "goldPrice": "1899", 102 | "platinumPrice": "1899", 103 | "diamondPrice": "1899", 104 | "limitedVipPrice": "899", 105 | "promotionPrice": "699", 106 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 107 | "promotionDesc": "促销描述", 108 | "count": "2", 109 | "collections": "2222", 110 | "comments": "1111", 111 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 112 | "status": "0" 113 | },{ 114 | "productId": "1001", 115 | "productName": "碎花连衣裙", 116 | "advertWord": "", 117 | "brandNameCN": "菲拉格慕", 118 | "brandNameEN": "SALVATORE FERRAGAMO", 119 | "brandNo": "B0084", 120 | "marketPrice": "1899", 121 | "limitedPrice": "1899", 122 | "goldPrice": "1899", 123 | "platinumPrice": "1899", 124 | "diamondPrice": "1899", 125 | "limitedVipPrice": "899", 126 | "promotionPrice": "699", 127 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 128 | "promotionDesc": "促销描述", 129 | "count": "2", 130 | "collections": "1111", 131 | "comments": "1111", 132 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 133 | "status": "0" 134 | },{ 135 | "productId": "1001", 136 | "productName": "碎花连衣裙", 137 | "advertWord": "", 138 | "brandNameCN": "菲拉格慕", 139 | "brandNameEN": "SALVATORE FERRAGAMO", 140 | "brandNo": "B0084", 141 | "marketPrice": "1899", 142 | "limitedPrice": "1899", 143 | "goldPrice": "1899", 144 | "platinumPrice": "1899", 145 | "diamondPrice": "1899", 146 | "limitedVipPrice": "899", 147 | "promotionPrice": "699", 148 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 149 | "promotionDesc": "促销描述", 150 | "count": "2", 151 | "collections": "2222", 152 | "comments": "1111", 153 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 154 | "status": "0" 155 | },{ 156 | "productId": "1001", 157 | "productName": "碎花连衣裙", 158 | "advertWord": "", 159 | "brandNameCN": "菲拉格慕", 160 | "brandNameEN": "SALVATORE FERRAGAMO", 161 | "brandNo": "B0084", 162 | "marketPrice": "1899", 163 | "limitedPrice": "1899", 164 | "goldPrice": "1899", 165 | "platinumPrice": "1899", 166 | "diamondPrice": "1899", 167 | "limitedVipPrice": "899", 168 | "promotionPrice": "699", 169 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 170 | "promotionDesc": "促销描述", 171 | "count": "2", 172 | "collections": "1111", 173 | "comments": "1111", 174 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 175 | "status": "0" 176 | },{ 177 | "productId": "1001", 178 | "productName": "碎花连衣裙", 179 | "advertWord": "", 180 | "brandNameCN": "菲拉格慕", 181 | "brandNameEN": "SALVATORE FERRAGAMO", 182 | "brandNo": "B0084", 183 | "marketPrice": "1899", 184 | "limitedPrice": "1899", 185 | "goldPrice": "1899", 186 | "platinumPrice": "1899", 187 | "diamondPrice": "1899", 188 | "limitedVipPrice": "899", 189 | "promotionPrice": "699", 190 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 191 | "promotionDesc": "促销描述", 192 | "count": "2", 193 | "collections": "2222", 194 | "comments": "1111", 195 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 196 | "status": "0" 197 | },{ 198 | "productId": "1001", 199 | "productName": "碎花连衣裙", 200 | "advertWord": "", 201 | "brandNameCN": "菲拉格慕", 202 | "brandNameEN": "SALVATORE FERRAGAMO", 203 | "brandNo": "B0084", 204 | "marketPrice": "1899", 205 | "limitedPrice": "1899", 206 | "goldPrice": "1899", 207 | "platinumPrice": "1899", 208 | "diamondPrice": "1899", 209 | "limitedVipPrice": "899", 210 | "promotionPrice": "699", 211 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 212 | "promotionDesc": "促销描述", 213 | "count": "2", 214 | "collections": "1111", 215 | "comments": "1111", 216 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 217 | "status": "0" 218 | },{ 219 | "productId": "1001", 220 | "productName": "碎花连衣裙", 221 | "advertWord": "", 222 | "brandNameCN": "菲拉格慕", 223 | "brandNameEN": "SALVATORE FERRAGAMO", 224 | "brandNo": "B0084", 225 | "marketPrice": "1899", 226 | "limitedPrice": "1899", 227 | "goldPrice": "1899", 228 | "platinumPrice": "1899", 229 | "diamondPrice": "1899", 230 | "limitedVipPrice": "899", 231 | "promotionPrice": "699", 232 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 233 | "promotionDesc": "促销描述", 234 | "count": "2", 235 | "collections": "2222", 236 | "comments": "1111", 237 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 238 | "status": "0" 239 | },{ 240 | "productId": "1001", 241 | "productName": "碎花连衣裙", 242 | "advertWord": "", 243 | "brandNameCN": "菲拉格慕", 244 | "brandNameEN": "SALVATORE FERRAGAMO", 245 | "brandNo": "B0084", 246 | "marketPrice": "1899", 247 | "limitedPrice": "1899", 248 | "goldPrice": "1899", 249 | "platinumPrice": "1899", 250 | "diamondPrice": "1899", 251 | "limitedVipPrice": "899", 252 | "promotionPrice": "699", 253 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 254 | "promotionDesc": "促销描述", 255 | "count": "2", 256 | "collections": "1111", 257 | "comments": "1111", 258 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 259 | "status": "0" 260 | },{ 261 | "productId": "1001", 262 | "productName": "碎花连衣裙", 263 | "advertWord": "", 264 | "brandNameCN": "菲拉格慕", 265 | "brandNameEN": "SALVATORE FERRAGAMO", 266 | "brandNo": "B0084", 267 | "marketPrice": "1899", 268 | "limitedPrice": "1899", 269 | "goldPrice": "1899", 270 | "platinumPrice": "1899", 271 | "diamondPrice": "1899", 272 | "limitedVipPrice": "899", 273 | "promotionPrice": "699", 274 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 275 | "promotionDesc": "促销描述", 276 | "count": "2", 277 | "collections": "2222", 278 | "comments": "1111", 279 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 280 | "status": "0" 281 | },{ 282 | "productId": "1001", 283 | "productName": "碎花连衣裙", 284 | "advertWord": "", 285 | "brandNameCN": "菲拉格慕", 286 | "brandNameEN": "SALVATORE FERRAGAMO", 287 | "brandNo": "B0084", 288 | "marketPrice": "1899", 289 | "limitedPrice": "1899", 290 | "goldPrice": "1899", 291 | "platinumPrice": "1899", 292 | "diamondPrice": "1899", 293 | "limitedVipPrice": "899", 294 | "promotionPrice": "699", 295 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 296 | "promotionDesc": "促销描述", 297 | "count": "2", 298 | "collections": "1111", 299 | "comments": "1111", 300 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 301 | "status": "0" 302 | },{ 303 | "productId": "1001", 304 | "productName": "碎花连衣裙", 305 | "advertWord": "", 306 | "brandNameCN": "菲拉格慕", 307 | "brandNameEN": "SALVATORE FERRAGAMO", 308 | "brandNo": "B0084", 309 | "marketPrice": "1899", 310 | "limitedPrice": "1899", 311 | "goldPrice": "1899", 312 | "platinumPrice": "1899", 313 | "diamondPrice": "1899", 314 | "limitedVipPrice": "899", 315 | "promotionPrice": "699", 316 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 317 | "promotionDesc": "促销描述", 318 | "count": "2", 319 | "collections": "2222", 320 | "comments": "1111", 321 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 322 | "status": "0" 323 | },{ 324 | "productId": "1001", 325 | "productName": "碎花连衣裙", 326 | "advertWord": "", 327 | "brandNameCN": "菲拉格慕", 328 | "brandNameEN": "SALVATORE FERRAGAMO", 329 | "brandNo": "B0084", 330 | "marketPrice": "1899", 331 | "limitedPrice": "1899", 332 | "goldPrice": "1899", 333 | "platinumPrice": "1899", 334 | "diamondPrice": "1899", 335 | "limitedVipPrice": "899", 336 | "promotionPrice": "699", 337 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 338 | "promotionDesc": "促销描述", 339 | "count": "2", 340 | "collections": "1111", 341 | "comments": "1111", 342 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 343 | "status": "0" 344 | },{ 345 | "productId": "1001", 346 | "productName": "碎花连衣裙", 347 | "advertWord": "", 348 | "brandNameCN": "菲拉格慕", 349 | "brandNameEN": "SALVATORE FERRAGAMO", 350 | "brandNo": "B0084", 351 | "marketPrice": "1899", 352 | "limitedPrice": "1899", 353 | "goldPrice": "1899", 354 | "platinumPrice": "1899", 355 | "diamondPrice": "1899", 356 | "limitedVipPrice": "899", 357 | "promotionPrice": "699", 358 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 359 | "promotionDesc": "促销描述", 360 | "count": "2", 361 | "collections": "2222", 362 | "comments": "1111", 363 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 364 | "status": "0" 365 | },{ 366 | "productId": "1001", 367 | "productName": "碎花连衣裙", 368 | "advertWord": "", 369 | "brandNameCN": "菲拉格慕", 370 | "brandNameEN": "SALVATORE FERRAGAMO", 371 | "brandNo": "B0084", 372 | "marketPrice": "1899", 373 | "limitedPrice": "1899", 374 | "goldPrice": "1899", 375 | "platinumPrice": "1899", 376 | "diamondPrice": "1899", 377 | "limitedVipPrice": "899", 378 | "promotionPrice": "699", 379 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 380 | "promotionDesc": "促销描述", 381 | "count": "2", 382 | "collections": "1111", 383 | "comments": "1111", 384 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 385 | "status": "0" 386 | },{ 387 | "productId": "1001", 388 | "productName": "碎花连衣裙", 389 | "advertWord": "", 390 | "brandNameCN": "菲拉格慕", 391 | "brandNameEN": "SALVATORE FERRAGAMO", 392 | "brandNo": "B0084", 393 | "marketPrice": "1899", 394 | "limitedPrice": "1899", 395 | "goldPrice": "1899", 396 | "platinumPrice": "1899", 397 | "diamondPrice": "1899", 398 | "limitedVipPrice": "899", 399 | "promotionPrice": "699", 400 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 401 | "promotionDesc": "促销描述", 402 | "count": "2", 403 | "collections": "2222", 404 | "comments": "1111", 405 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 406 | "status": "0" 407 | },{ 408 | "productId": "1001", 409 | "productName": "碎花连衣裙", 410 | "advertWord": "", 411 | "brandNameCN": "菲拉格慕", 412 | "brandNameEN": "SALVATORE FERRAGAMO", 413 | "brandNo": "B0084", 414 | "marketPrice": "1899", 415 | "limitedPrice": "1899", 416 | "goldPrice": "1899", 417 | "platinumPrice": "1899", 418 | "diamondPrice": "1899", 419 | "limitedVipPrice": "899", 420 | "promotionPrice": "699", 421 | "isSupportDiscount": "是否支持会员权益0,否;1,是", 422 | "promotionDesc": "促销描述", 423 | "count": "2", 424 | "collections": "1111", 425 | "comments": "1111", 426 | "pic": "http://pic14.shangpin.com/e/s/14/08/20/20140820191723534713-10-10.jpg", 427 | "status": "0" 428 | } 429 | ] 430 | } 431 | } -------------------------------------------------------------------------------- /SCJSONUtil/SCJSONUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCJSONUtil.m 3 | // 4 | // Created by xuqianlong on 15/9/3. 5 | // Copyright (c) 2015年 Mac. All rights reserved. 6 | // 7 | 8 | #import "SCJSONUtil.h" 9 | #import "objc/runtime.h" 10 | #import "scutil.h" 11 | 12 | static BOOL s_SCJSONUtilLogOn = NO; 13 | 14 | void SCJSONUtilLog(BOOL on) { 15 | s_SCJSONUtilLogOn = on; 16 | } 17 | 18 | BOOL isSCJSONUtilLogOn(void) { 19 | return s_SCJSONUtilLogOn; 20 | } 21 | 22 | #define SCJSONLog(...) do{ \ 23 | if (s_SCJSONUtilLogOn) { \ 24 | NSLog(__VA_ARGS__); \ 25 | } \ 26 | }while(0) 27 | 28 | #define SCJSONError(...) do{ \ 29 | NSLog(__VA_ARGS__); \ 30 | }while(0) 31 | 32 | typedef NS_ENUM(NSUInteger, QLPropertyType) { 33 | QLPropertyTypeUnknow, 34 | QLPropertyTypeId = '*', 35 | QLPropertyTypeObj = '@', 36 | QLPropertyTypeFloat = 'f', 37 | QLPropertyTypeDouble = 'd', 38 | QLPropertyTypeBOOL = 'B', 39 | QLPropertyTypeBool = QLPropertyTypeBOOL, 40 | QLPropertyTypeInt8 = 'c', 41 | QLPropertyTypeUInt8 = 'C', 42 | QLPropertyTypeInt16 = 's', 43 | QLPropertyTypeUInt16 = 'S', 44 | QLPropertyTypeInt32 = 'i', 45 | QLPropertyTypeUInt32 = 'I', 46 | QLPropertyTypeLong32 = 'l', //32位机器 long 型 47 | QLPropertyTypeULong32 = 'L', //32位机器 unsigned long 型 48 | QLPropertyTypeInt64 = 'q', //32位机器 long long 类型;64位机器 long long 和 long 类型 49 | QLPropertyTypeUInt64 = 'Q' //32位机器 unsigned long long 类型;64位机器 unsigned long long 和 unsigned long 类型 50 | }; 51 | 52 | typedef struct QLPropertyDescS { 53 | QLPropertyType type; 54 | char * clazz; 55 | } QLPropertyDesc; 56 | 57 | #pragma mark - C Functions -Begin- 58 | 59 | #pragma mark 【Utils】 60 | 61 | static void *QLMallocInit(size_t __size) { 62 | void *p = malloc(__size); 63 | memset(p, 0, __size); 64 | return p; 65 | } 66 | 67 | static QLPropertyDesc * QLPropertyDescForClassProperty(Class clazz, const char *key) { 68 | objc_property_t property = class_getProperty(clazz, key); 69 | if (NULL == property) { 70 | return NULL; 71 | } 72 | // 2.成员类型 73 | const char *encodedType = property_getAttributes(property); 74 | //TB,N,V_boolType 75 | //T@"NSString",C,N,V_stringType 76 | //T@,&,N,V_idType 77 | //T@"NSString",R,C description debugDescription 78 | //T#,R superclass 79 | //TQ,R hash 80 | NSString *encodedStr = [[NSString alloc] initWithCString:encodedType encoding:NSUTF8StringEncoding]; 81 | NSArray *items = [encodedStr componentsSeparatedByString:@","]; 82 | //skip the readonly property. fix [ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key description.' 83 | if ([items containsObject:@"R"]) { 84 | return NULL; 85 | } 86 | 87 | NSString *varFullType = [items firstObject]; 88 | 89 | if (varFullType.length >= 2) { 90 | const char iType = [varFullType characterAtIndex:1]; 91 | switch (iType) { 92 | case QLPropertyTypeObj: 93 | { 94 | //属性是对象类型,这里取出对象的类型,id取不出来; 95 | bool isID = [@"T@" isEqualToString:varFullType]; 96 | if (isID) { 97 | QLPropertyDesc *desc = (QLPropertyDesc *)QLMallocInit(sizeof(QLPropertyDesc)); 98 | desc->type = QLPropertyTypeId; 99 | return desc; 100 | } else { 101 | NSString *varType = [varFullType substringFromIndex:2]; 102 | varType = [varType stringByReplacingOccurrencesOfString:@"\"" withString:@""]; 103 | QLPropertyDesc *desc = (QLPropertyDesc *)QLMallocInit(sizeof(QLPropertyDesc)); 104 | char *pclazz = (char *)QLMallocInit(varType.length + 1); 105 | strcpy(pclazz, varType.UTF8String); 106 | desc->clazz = pclazz; 107 | desc->type = iType; 108 | return desc; 109 | } 110 | } 111 | break; 112 | case QLPropertyTypeFloat: 113 | case QLPropertyTypeDouble: 114 | case QLPropertyTypeBOOL: 115 | case QLPropertyTypeInt8: 116 | case QLPropertyTypeUInt8: 117 | case QLPropertyTypeInt16: 118 | case QLPropertyTypeUInt16: 119 | case QLPropertyTypeInt32: 120 | case QLPropertyTypeUInt32: 121 | case QLPropertyTypeLong32: 122 | case QLPropertyTypeULong32: 123 | case QLPropertyTypeInt64: 124 | case QLPropertyTypeUInt64: 125 | { 126 | QLPropertyDesc *desc = QLMallocInit(sizeof(QLPropertyDesc));//must init!!! iphone 5 crash,clazz is empty string : '' ; 127 | desc->type = iType; 128 | return desc; 129 | } 130 | break; 131 | 132 | default: // #:^ igonre:Class,SEL,Method... 133 | { 134 | SCJSONLog(@"unrecognized type:%c",iType); 135 | } 136 | break; 137 | } 138 | } 139 | return NULL; 140 | } 141 | 142 | #pragma mark 【ValueTransfer】 143 | 144 | static NSString * QLValueTransfer2NSString(id value) { 145 | return [value description]; 146 | } 147 | 148 | static NSNumber * QLValueTransfer2NSNumber(id value) { 149 | if ([value isKindOfClass:[NSString class]]) { 150 | NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; 151 | return [numberFormatter numberFromString:value]; 152 | } else if ([value isKindOfClass:[NSNumber class]]) { 153 | return value; 154 | } 155 | return nil; 156 | } 157 | 158 | static NSDecimalNumber * QLValueTransfer2NSDecimalNumber(id value) { 159 | if ([value isKindOfClass:[NSString class]]) { 160 | return [NSDecimalNumber decimalNumberWithString:value]; 161 | } 162 | return nil; 163 | } 164 | 165 | static NSURL * QLValueTransfer2NSURL(id value) { 166 | if ([value isKindOfClass:[NSString class]]) { 167 | NSString *str = (NSString *)value; 168 | if ([str hasPrefix:@"file://"]) { 169 | str = [str stringByReplacingOccurrencesOfString:@"file://" withString:@""]; 170 | return [NSURL fileURLWithPath:str]; 171 | } 172 | return [NSURL URLWithString:str]; 173 | } 174 | return nil; 175 | } 176 | 177 | #pragma mark C Functions -End- 178 | #pragma mark - 179 | 180 | @implementation NSObject (SCJSON2Model) 181 | 182 | - (void)sc_findPropertyFromCollideKeyMap:(NSString *)serverKey 183 | forValue:(id)serverValue 184 | completion:(void(^)(NSString *, NSString *,id))comp 185 | { 186 | id_self = (id)self; 187 | if ([_self respondsToSelector:@selector(sc_collideKeysMap)]) { 188 | NSDictionary *map = [_self sc_collideKeysMap]; 189 | NSString * mappedName = map[serverKey]; 190 | //严格匹配上了,意味着很明确要映射 191 | if (mappedName) { 192 | if (comp) { 193 | //此时仍然使用 serverKey 作为后续的查询 Model 类名的 key 194 | comp(mappedName,serverKey,serverValue); 195 | } 196 | return; 197 | } 198 | //下面是支持 keypath 查找的逻辑 199 | __block BOOL matched = NO; 200 | [map enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull customServerKey, NSString * _Nonnull mappedPropertyName, BOOL * _Nonnull stop) { 201 | //自定义了属性名,将服务器返回的值根据keypath进一步查找,然后对应到自定义的属性名上 202 | if ([customServerKey hasPrefix:serverKey]) { 203 | NSRange range = [customServerKey rangeOfString:@"."]; 204 | if (range.location != NSNotFound) { 205 | if([serverValue isKindOfClass:[NSDictionary class]]) { 206 | NSString *targetKey = [customServerKey substringFromIndex:range.location + range.length]; 207 | id mappedValue = [serverValue valueForKeyPath:targetKey]; 208 | //确定一个keypath查找就invoke下;但仍旧继续遍历,以处理通过多个 keypath 查找同一个字段下的多个子字段情况 @{@"data.a":"a",@"data.b":"b"} 209 | if (comp) { 210 | comp(mappedPropertyName,customServerKey,mappedValue); 211 | } 212 | matched = YES; 213 | } 214 | } 215 | } 216 | }]; 217 | //遍历完了还没匹配上,说明没有配置映射 218 | if (!matched) { 219 | if (comp) { 220 | comp(nil,serverKey,serverValue); 221 | } 222 | } 223 | } else { 224 | //默认情况下,把服务器返回的字段名,当做model的属性名 225 | if (comp) { 226 | comp(nil,serverKey,serverValue); 227 | } 228 | } 229 | } 230 | 231 | - (void)sc_resolveunDefinedKey:(NSString *)propertyName 232 | forValue:(id)propertyValue 233 | refObj:(id)refObj 234 | completion:(void(^)(NSString *, NSString *,id))comp 235 | { 236 | NSString *outName = propertyName; 237 | id outValue = propertyValue; 238 | 239 | id_self = (id)self; 240 | if ([_self respondsToSelector:@selector(sc_unDefinedKey:forValue:refObj:)] && [_self sc_unDefinedKey:&outName forValue:&outValue refObj:refObj]) { 241 | SCJSONLog(@"mapped server key:%@->%@.%@",propertyName,NSStringFromClass([self class]),outName); 242 | if (outName.length > 0) { 243 | if (comp) { 244 | //此时使用自定义映射属性名 outName 作为后续的查询 Model 类名的 key 245 | comp(outName,outName,outValue); 246 | } 247 | return; 248 | } 249 | } else { 250 | NSString *propertyClass = NSStringFromClass([propertyValue class]); 251 | if ([propertyClass hasPrefix:@"__"]) { 252 | propertyClass = NSStringFromClass([propertyValue superclass]); 253 | } 254 | SCJSONLog(@"igonred %@.%@,add property use %@ * %@",NSStringFromClass([self class]),propertyName,propertyClass,propertyName); 255 | } 256 | } 257 | 258 | - (void)sc_resolveProperty:(NSString *)propertyName 259 | forValue:(id)propertyValue 260 | refObj:(id)refObj 261 | completion:(void(^)(QLPropertyDesc *, NSString *, NSString *,id))comp 262 | { 263 | /* 264 | 为了支持 keypath 查找,只能优先查找 sc_collideKeysMap,否则会出现类型不匹配,出现没解析的情况! 265 | 比如:{@"activity.on" : @"activity"} 266 | */ 267 | [self sc_findPropertyFromCollideKeyMap:propertyName 268 | forValue:propertyValue 269 | completion:^(NSString *pName, NSString *mKey, id mValue) { 270 | QLPropertyDesc * pdesc = NULL; 271 | if (pName) { 272 | //通过sc_collideKeysMap提供了 273 | pdesc = QLPropertyDescForClassProperty([self class], [pName UTF8String]); 274 | } else { 275 | //当sc_collideKeysMap没提供时,就把服务区返回字段当做 Model 属性名进行试探 276 | pdesc = QLPropertyDescForClassProperty([self class], [propertyName UTF8String]); 277 | } 278 | 279 | if (pdesc) { 280 | if (pName) { 281 | comp(pdesc,pName,mKey,mValue); 282 | } else { 283 | comp(pdesc,propertyName,propertyName,propertyValue); 284 | } 285 | } else { 286 | //获取属性类型,获取不到时走 sc_unDefinedKey 来补救 287 | [self sc_resolveunDefinedKey:propertyName 288 | forValue:propertyValue 289 | refObj:refObj 290 | completion:^(NSString *aName,NSString *aKey,id aValue) { 291 | if (aName) { 292 | QLPropertyDesc * pdesc = QLPropertyDescForClassProperty([self class], [aName UTF8String]); 293 | //补救的属性存在? 294 | if (pdesc) { 295 | comp(pdesc,aName,aKey,aValue); 296 | } 297 | } 298 | }]; 299 | } 300 | }]; 301 | } 302 | 303 | - (void)sc_autoMatchValue:(id)serverValue 304 | forKey:(NSString *)serverKey 305 | refObj:(id)refObj 306 | { 307 | //1、处理属性名(顺序:根据sc_collideKeysMap获取;服务器返回的字段名;通过sc_unDefinedKey方法) 308 | [self sc_resolveProperty:serverKey 309 | forValue:serverValue 310 | refObj:refObj 311 | completion:^(QLPropertyDesc *pdesc,NSString *propertyName,NSString *modelKey,id mappedValue) { 312 | if (pdesc) { 313 | //属性描述不空,则表示有这个属性 314 | id instance = (id)self; 315 | 316 | //2、进行自动匹配赋值之前,再给客户端一次机会,可根据业务逻辑自行处理 317 | if ([instance respondsToSelector:@selector(sc_key:beforeAssignedValue:refObj:)]) { 318 | mappedValue = [instance sc_key:propertyName beforeAssignedValue:mappedValue refObj:refObj]; 319 | } 320 | 321 | //3、进入类型自动匹配流程 322 | do { 323 | //属性是 id 类型时,无法进行值的类型匹配 324 | if (pdesc->type == QLPropertyTypeId) { 325 | if (mappedValue) { 326 | [self setValue:mappedValue forKey:propertyName]; 327 | } 328 | break; 329 | } 330 | 331 | //3.1、匹配数组类型 332 | if ([mappedValue isKindOfClass:[NSArray class]]) { 333 | 334 | if (pdesc->type == QLPropertyTypeObj && (QLCStrEqual((char *)pdesc->clazz, "NSMutableArray") || QLCStrEqual((char *)pdesc->clazz, "NSArray") )) { 335 | //优先从 collideKeyModelMap 获取 Model 类名 336 | NSString *modleName = nil; 337 | if ([instance respondsToSelector:@selector(sc_collideKeyModelMap)]) { 338 | modleName = [[instance sc_collideKeyModelMap]objectForKey:modelKey]; 339 | } 340 | 341 | NSArray *objs = nil; 342 | if (modleName) { 343 | Class clazz = NSClassFromString(modleName); 344 | if (clazz) { 345 | objs = [clazz sc_instanceArrFormArray:mappedValue]; 346 | } else { 347 | SCJSONLog(@"⚠️⚠️⚠️[%@ Class] is undefined!",modleName); 348 | break; 349 | } 350 | } else { 351 | //当获取不到 Model 类名时不解析 352 | objs = [NSArray arrayWithArray:mappedValue]; 353 | } 354 | 355 | char * pclazz = pdesc->clazz; 356 | //如果属性是可变的,那么做个可变处理 357 | if (QLCStrEqual(pclazz, "NSMutableArray")) { 358 | objs = [NSMutableArray arrayWithArray:objs]; 359 | } 360 | if (objs) { 361 | [self setValue:objs forKey:propertyName]; 362 | } 363 | } else { 364 | //忽略掉 Model 属性不是 NSMutableArray/NSArray 的情况 365 | SCJSONLog(@"⚠️⚠️ %@.%@ may be: (%@*)%@",NSStringFromClass([self class]),propertyName,NSStringFromClass([mappedValue class]),propertyName); 366 | } 367 | } 368 | //3.2、匹配字典类型 369 | else if ([mappedValue isKindOfClass:[NSDictionary class]]) { 370 | //如果 property 是字典类型则直接赋值,否则执行 model 解析 371 | if (pdesc->type == QLPropertyTypeObj) { 372 | if (QLCStrEqual((char *)pdesc->clazz, "NSMutableDictionary")) { 373 | [self setValue:[mappedValue mutableCopy] forKey:propertyName]; 374 | } else if (QLCStrEqual((char *)pdesc->clazz, "NSDictionary")) { 375 | [self setValue:mappedValue forKey:propertyName]; 376 | } else { 377 | Class clazz = objc_getClass(pdesc->clazz); 378 | if (clazz) { 379 | id value = [clazz sc_instanceFormDic:mappedValue]; 380 | if (value) { 381 | [self setValue:value forKey:propertyName]; 382 | } 383 | } 384 | } 385 | } else { 386 | //忽略掉 Model 属性不是 NSMutableDictionary/NSDictionary 的情况 387 | SCJSONLog(@"⚠️⚠️ %@.%@ may be: (%@*)%@",NSStringFromClass([self class]),propertyName,NSStringFromClass([mappedValue class]),propertyName); 388 | } 389 | } 390 | //3.3、匹配非 NULL 类型 391 | else if (![mappedValue isKindOfClass:[NSNull class]]) { 392 | //简单非空类型 id,NSStirng,NSNumber,int,bool,float... 393 | switch (pdesc->type) { 394 | case QLPropertyTypeObj: 395 | { 396 | id r = nil; 397 | const char *pClazz = pdesc->clazz; 398 | //目标类型可能是id类型,直接赋值 399 | if (!pClazz) { 400 | r = mappedValue; 401 | } else if (QLCStrEqual((char *)pClazz, (char *)object_getClassName(mappedValue))) { 402 | //目标类型和值类型相同,则直接赋值 403 | r = mappedValue; 404 | } else if (QLCStrEqual((char *)pClazz, "NSString")) { 405 | //目标类型是NSString 406 | r = QLValueTransfer2NSString(mappedValue); 407 | } else if (QLCStrEqual((char *)pClazz, "NSMutableString")) { 408 | //目标类型是NSMutableString 409 | NSString *value = QLValueTransfer2NSString(mappedValue); 410 | r = [NSMutableString stringWithString:value]; 411 | } else if (QLCStrEqual((char *)pClazz, "NSNumber")) { 412 | //目标类型是NSNumber 413 | r = QLValueTransfer2NSNumber(mappedValue); 414 | } else if (QLCStrEqual((char *)pClazz, "NSDecimalNumber")) { 415 | //目标类型是NSDecimalNumber 416 | r = QLValueTransfer2NSDecimalNumber(mappedValue); 417 | } else if (QLCStrEqual((char *)pClazz, "NSURL")) { 418 | //目标类型是NSURL 419 | r = QLValueTransfer2NSURL(mappedValue); 420 | } else { 421 | SCJSONError(@"unrecognized type (%s)%@ for (%@)",pClazz,propertyName,NSStringFromClass([mappedValue class])); 422 | } 423 | 424 | if (r) { 425 | [self setValue:r forKey:propertyName]; 426 | } 427 | } 428 | break; 429 | //因为kvc本身需要的value是id类型,所以对于基本数据类型不处理,而是交给系统 KVC 处理; 430 | case QLPropertyTypeFloat: 431 | case QLPropertyTypeDouble: 432 | case QLPropertyTypeBOOL: 433 | case QLPropertyTypeInt8: 434 | case QLPropertyTypeUInt8: 435 | case QLPropertyTypeInt16: 436 | case QLPropertyTypeUInt16: 437 | case QLPropertyTypeInt32: 438 | case QLPropertyTypeUInt32: 439 | case QLPropertyTypeLong32: 440 | case QLPropertyTypeULong32: 441 | case QLPropertyTypeInt64: 442 | case QLPropertyTypeUInt64: 443 | { 444 | NSNumber *numberValue = mappedValue; 445 | //ios 8, -[__NSCFString longValue]: unrecognized selector 446 | if ([numberValue isKindOfClass:[NSString class]]) { 447 | numberValue = QLValueTransfer2NSNumber(mappedValue); 448 | } 449 | //could not set nil as the value for the scalar key(int,float,...) 450 | if (numberValue) { 451 | [self setValue:numberValue forKey:propertyName]; 452 | } 453 | } 454 | break; 455 | default: 456 | break; 457 | } 458 | } 459 | } while (0); 460 | 461 | //4、释放内存 462 | if (NULL != pdesc) { 463 | if (NULL != pdesc->clazz) { 464 | free((void *)pdesc->clazz); 465 | pdesc->clazz = NULL; 466 | } 467 | free(pdesc); 468 | pdesc = NULL; 469 | } 470 | } 471 | }]; 472 | } 473 | 474 | - (void)sc_assembleDataFormDic:(NSDictionary *)dic 475 | { 476 | [self sc_assembleDataFormDic:dic refObj:nil]; 477 | } 478 | 479 | - (void)sc_assembleDataFormDic:(NSDictionary *)dic refObj:(id)refObj 480 | { 481 | [dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 482 | [self sc_autoMatchValue:obj forKey:key refObj:refObj]; 483 | }]; 484 | 485 | id instance = (id)self; 486 | if ([instance respondsToSelector:@selector(sc_willFinishConvert:refObj:)]) { 487 | [instance sc_willFinishConvert:dic refObj:refObj]; 488 | } 489 | } 490 | 491 | + (id)sc_instanceFromValue:(id)json refObj:(id)refObj 492 | { 493 | if ([json isKindOfClass:[NSDictionary class]]) { 494 | NSObject *obj = [[self alloc]init]; 495 | [obj sc_assembleDataFormDic:json refObj:refObj]; 496 | return obj; 497 | } 498 | if ([json isKindOfClass:[NSArray class]]) { 499 | NSArray *jsonArr = json; 500 | 501 | if (!jsonArr || jsonArr.count == 0) { 502 | return nil; 503 | } 504 | 505 | NSMutableArray *modelArr = [[NSMutableArray alloc]initWithCapacity:3]; 506 | [jsonArr enumerateObjectsUsingBlock:^(id json, NSUInteger idx, BOOL *stop) { 507 | id instance = [self sc_instanceFromValue:json refObj:refObj]; 508 | if (instance) { 509 | [modelArr addObject:instance]; 510 | } else { 511 | SCJSONError(@"WTF?can't convert json [%@] to [%@]",json,NSStringFromClass([self class])); 512 | } 513 | }]; 514 | return [NSArray arrayWithArray:modelArr]; 515 | } 516 | if ([self class] == [NSNumber class]) { 517 | return QLValueTransfer2NSNumber(json); 518 | } 519 | if ([self class] == [NSString class]) { 520 | return QLValueTransfer2NSString(json); 521 | } 522 | if ([self class] == [NSURL class]) { 523 | return QLValueTransfer2NSURL(json); 524 | } 525 | if ([self class] == [NSDecimalNumber class]) { 526 | return QLValueTransfer2NSDecimalNumber(json); 527 | } 528 | SCJSONError(@"can't convert json [%@] to [%@]",json,NSStringFromClass([self class])); 529 | return nil; 530 | } 531 | 532 | + (id)sc_instanceFromValue:(id)json 533 | { 534 | return [self sc_instanceFromValue:json refObj:nil]; 535 | } 536 | 537 | + (instancetype)sc_instanceFormDic:(NSDictionary *)jsonDic 538 | { 539 | return [self sc_instanceFromValue:jsonDic]; 540 | } 541 | 542 | + (NSArray *)sc_instanceArrFormArray:(NSArray *)jsonArr 543 | { 544 | return [self sc_instanceFromValue:jsonArr]; 545 | } 546 | 547 | @end 548 | 549 | #pragma mark - JOSNUtil public c functions 550 | 551 | id SCFindJSONwithKeyPathArr(NSArray *pathArr, NSDictionary *json){ 552 | if (!json) { 553 | return nil; 554 | } 555 | if (!pathArr || pathArr.count == 0) { 556 | return json; 557 | } 558 | NSMutableArray *pathArr2 = [NSMutableArray arrayWithArray:pathArr]; 559 | 560 | while ([pathArr2 firstObject] && [[pathArr2 firstObject] description].length == 0) { 561 | [pathArr2 removeObjectAtIndex:0]; 562 | } 563 | if ([pathArr2 firstObject]) { 564 | json = [json objectForKey:[pathArr2 firstObject]]; 565 | [pathArr2 removeObjectAtIndex:0]; 566 | return SCFindJSONwithKeyPathArr(pathArr2, json); 567 | } else { 568 | return json; 569 | } 570 | } 571 | 572 | id SCFindJSONwithKeyPathV2(NSString *keyPath, NSDictionary *JSON, NSString *separator){ 573 | if (!keyPath || keyPath.length == 0) { 574 | return JSON; 575 | } 576 | if (!separator) { 577 | separator = @""; 578 | } 579 | NSArray *pathArr = [keyPath componentsSeparatedByString:separator]; 580 | return SCFindJSONwithKeyPathArr(pathArr, JSON); 581 | } 582 | 583 | id SCFindJSONwithKeyPath(NSString *keyPath, NSDictionary *json){ 584 | return SCFindJSONwithKeyPathV2(keyPath, json, @"/"); 585 | } 586 | 587 | id SCJSON2ModelV2(id json, NSString *modelName, id refObj){ 588 | Class clazz = NSClassFromString(modelName); 589 | return [clazz sc_instanceFromValue:json refObj:refObj]; 590 | } 591 | 592 | id SCJSON2Model(id json, NSString *modelName){ 593 | return SCJSON2ModelV2(json, modelName, nil); 594 | } 595 | 596 | NSString *JSON2String(id json, BOOL prettyPrinted) { 597 | if (@available(iOS 13.0,macos 10.15, watchos 6.0, tvos 13.0, *)) { 598 | NSJSONWritingOptions opts = NSJSONWritingWithoutEscapingSlashes; 599 | if (prettyPrinted) { 600 | opts |= NSJSONWritingPrettyPrinted; 601 | } 602 | NSData *data = [NSJSONSerialization dataWithJSONObject:json options:opts error:nil]; 603 | if (data) { 604 | return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 605 | } 606 | } else { 607 | NSData *data = [NSJSONSerialization dataWithJSONObject:json options:prettyPrinted ? NSJSONWritingPrettyPrinted:0 error:nil]; 608 | if (data) { 609 | NSString *jsonStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 610 | return [jsonStr stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"]; 611 | } 612 | } 613 | return nil; 614 | } 615 | --------------------------------------------------------------------------------