Giter VIP home page Giter VIP logo

cleanjson's Introduction

CleanJSON

build Version Carthage compatible SPM support License Platform codebeat badge

继承自 JSONDecoder,在标准库源码基础上做了改动,以解决 JSONDecoder 各种解析失败的问题,如键值不存在,值为 null,类型不一致。

只需将 JSONDecoder 替换成 CleanJSONDecoder。

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Requirements

  • iOS 9.0
  • Swift 5.2

Installation

CleanJSON is available through CocoaPods or Carthage. To install it, simply add the following line to your Podfile or Cartfile:

Podfile

pod 'CleanJSON'

Cartfile

github "Pircate/CleanJSON"

Import

import CleanJSON

Usage

Normal

let decoder = CleanJSONDecoder()
try decoder.decode(Model.self, from: data)

// 支持直接解析符合 JSON 规范的字典和数组
try decoder.decode(Model.self, from: ["key": value])

Enum

对于枚举类型请遵循 CaseDefaultable 协议,如果解析失败会返回默认 case

Note: 枚举使用强类型解析,关联类型和数据类型不一致不会进行类型转换,会解析为默认 case

enum Enum: Int, Codable, CaseDefaultable {
    
    case case1
    case case2
    case case3
    
    static var defaultCase: Enum {
        return .case1
    }
}

Customize decoding strategy

可以通过 valueNotFoundDecodingStrategy 在值为 null 或类型不匹配的时候自定义解码,默认策略请看这里

struct CustomAdapter: JSONAdapter {
    
    // 由于 Swift 布尔类型不是非 0 即 true,所以默认没有提供类型转换。
    // 如果想实现 Int 转 Bool 可以自定义解码。
    func adapt(_ decoder: CleanDecoder) throws -> Bool {
        // 值为 null
        if decoder.decodeNil() {
            return false
        }
        
        if let intValue = try decoder.decodeIfPresent(Int.self) {
            // 类型不匹配,期望 Bool 类型,实际是 Int 类型
            return intValue != 0
        }
        
        return false
    }
    
    // 为避免精度丢失所以没有提供浮点型转整型
    // 可以通过下面适配器进行类型转换
    func adapt(_ decoder: CleanDecoder) throws -> Int {
        guard let doubleValue = try decoder.decodeIfPresent(Double.self) else { return 0 }
        
        return Int(doubleValue)
    }
    
    // 可选的 URL 类型解析失败的时候返回一个默认 url
    func adaptIfPresent(_ decoder: CleanDecoder) throws -> URL? {
        return URL(string: "https://google.com")
    }
}

decoder.valueNotFoundDecodingStrategy = .custom(CustomAdapter())

可以通过 JSONStringDecodingStrategy 将 JSON 格式的字符串自动转成 Codable 对象或数组

// 包含这些 key 的 JSON 字符串转成对象
decoder.jsonStringDecodingStrategy = .containsKeys([])

// 所有 JSON 字符串都转成对象
decoder.jsonStringDecodingStrategy = .all

keyDecodingStrategy 新增了一个自定义映射器,可以只映射指定 coding path 的 key

decoder.keyDecodingStrategy = .mapper([
    ["snake_case"]: "snakeCase",
    ["nested", "alpha"]: "a"
])

For Moya

使用 Moya.Response 自带的 map 方法解析,传入 CleanJSONDecoder

provider = MoyaProvider<GitHub>()
provider.request(.zen) { result in
    switch result {
    case let .success(response):
        let decoder = CleanJSONDecoder()
        let model = response.map(Model.self, using: decoder)
    case let .failure(error):
        // this means there was a network failure - either the request
        // wasn't sent (connectivity), or no response was received (server
        // timed out).  If the server responds with a 4xx or 5xx error, that
        // will be sent as a ".success"-ful response.
    }
}

For RxMoya

provider = MoyaProvider<GitHub>()

let decoder = CleanJSONDecoder()
provider.rx.request(.userProfile("ashfurrow"))
    .map(Model.self, using: decoder)
    .subscribe { event in
        switch event {
        case let .success(model):
            // do someting
        case let .error(error):
            print(error)
        }
    }

Author

Pircate, [email protected]

License

CleanJSON is available under the MIT license. See the LICENSE file for more info.

cleanjson's People

Contributors

pircate avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cleanjson's Issues

可以给某个key,单独设置默认值么?

struct CustomAdapterAAA: JSONAdapter {
public func adapt(_ decoder: CleanDecoder) throws -> String {
return "default string"
}
}
这个方法可以给这个decoder解析出来的所有string设置默认值,想问下,可以给key设置单独的默认值么,比如说一个model里有strA,想要默认值是“strA”
strB,想要默认值是“strB”
支持这样单独设置默认值么?

UUID类型字段改成var后无法解码

我用聚合笑话接口测试,他们返回的数据结构如下:

{
    "reason":"Success",
    "result":{
        "data":[
            {
                "content":"某先生是地",
                "hashId":"90B807CFEBF41",
                "unixtime":1418745227,
                "updatetime":"2014-12-16 23:53:47"
            },
            {
                "content":"有一天我看",
                "hashId":"206F5AC61F2A",
                "unixtime":1418745227,
                "updatetime":"2014-12-16 23:53:47"
            }
        ]
    }
}

下面是我创建的结构体

struct JuHeLaugh: Codable {
    var error_code: Int
    var reason: String
    var result: ResultModel
    
    struct ResultModel:Codable {
        
        var data: [LaughItem]
    
    }
}

struct LaughItem: Codable, Identifiable {
    let id: UUID = UUID()
    var content: String
}

请求代码

class Api {
    
    func getLaughs(complete: @escaping ([LaughItem]) -> ()){
        Alamofire.AF.request("http://v.juhe.cn/joke/content/list.php?sort=&page=&pagesize=&time=1418816972&key=4d9d1e5d2d284bc9e8e2428f43353f43")
        .responseString { response in
            guard let data = response.value else {
                return
            }
            print(data)
            do {
                let decoder = CleanJSONDecoder()
                let rst = try decoder.decode(JuHeLaugh.self, from: data.data(using:.utf8)!)
               
                print(rst.result)
                //complete(rst.result.data)
              // This should work
            } catch {
              print("error is \(error.localizedDescription)")
            }
        }
    }
}

本来这一切都没有问题,但是xcode会对LaughItem有一个警告

Immutable property will not be decoded because it is declared with an initial value which cannot be overwritten

会提示我将LaughItem改成如下的样子

struct LaughItem: Codable, Identifiable {
    var id: UUID
    var content: String
}

但这样一改之后,decode就出问题了,错误日志:

error is The data couldn’t be read because it isn’t in the correct format.

尽管我添加了一个自定义的UidAdapter,也无济于事。

struct UidAdapter: JSONAdapter {
    func adapt(_ decoder: CleanDecoder) throws -> UUID {
        return UUID()
    }
}

新的解码逻辑

do {
	let decoder = CleanJSONDecoder()
	decoder.valueNotFoundDecodingStrategy = .custom(UidAdapter())
	let rst = try decoder.decode(JuHeLaugh.self, from: data.data(using:.utf8)!)
	print(rst.result)
} catch {
	print("error is \(error.localizedDescription)")
}

解析的问题

数据源是Int类型 我用String接收 为什么会是nil

key定义成可选类型,也会解析为确定类型

Code

class TesModel: NSObject, Codable {
var str: String?
var subMod: TesSubModel?
}
class TesSubModel: NSObject, Codable {
var str: String?
}

问题

如果str、subModel不存在,或者类型不对,在解析后,
str = “”
subMod = TesSubModel()

因为model已经定义成可选型,而不是具体类型,此时如果返回的确定类型,而不是nil会感觉很奇怪,使用上也不方便,
比如要判断model是否存在,去做对应处理,应该是
if let model = subMod {
xxx
}
但是因为返回了确定类型,只是里面没值,就需要在做附加判断处理,比如
if let model = subMod, !model.str.isEmpty {
xxx
}

期望

定义成可选型后,model解析异常是可以返回nil,或者可以选择可以返回nil 还是确定型

decode结构体走了message send,导致crash

FATAL_SIGNAL SIGABRT fault_address:0x00000001ef024dd0
Thread 0 name: com.apple.main-thread
0libsystem_kernel.dylib
___abort_with_payload (in libsystem_kernel.dylib)
1libsystem_kernel.dylib
_abort_with_payload_wrapper_internal (in libsystem_kernel.dylib)
2libsystem_kernel.dylib
_abort_with_payload_wrapper_internal (in libsystem_kernel.dylib)
3libobjc.A.dylib
_objc_fatalv(unsigned long long, unsigned long long, char const*, char*) (in libobjc.A.dylib)
4libobjc.A.dylib
___objc_error (in libobjc.A.dylib)
5libobjc.A.dylib
_lookUpImpOrForward (in libobjc.A.dylib)
6libobjc.A.dylib
__objc_msgSend_uncached (in libobjc.A.dylib)
7libswiftCore.dylib
_swift_instantiateObjCClass (in libswiftCore.dylib)
8libswiftCore.dylib
_swift_initClassMetadataImpl(swift::TargetClassMetadataswift::InProcess, swift::ClassLayoutFlags, unsigned long, swift::TypeLayout const const*, unsigned long*, bool) (in libswiftCore.dylib)
9libswiftCore.dylib
type metadata completion function for _KeyedDecodingContainerBox (in libswiftCore.dylib)
10libswiftCore.dylib
swift::MetadataCacheEntryBase<(anonymous namespace)::GenericCacheEntry, void const*>::doInitialization(swift::ConcurrencyControl&, swift::MetadataCompletionQueueEntry*, swift::MetadataRequest) (in libswiftCore.dylib)
11libswiftCore.dylib
_swift_getGenericMetadata (in libswiftCore.dylib)
12libswiftCore.dylib
KeyedDecodingContainer.init(A1) (in libswiftCore.dylib)
1 MyProj
_CleanJSONDecoder.container(keyedBy: A.Type) (in MyProj) CleanJSONDecoder.swift:75
1 MyProj
protocol witness for Decoder.container
(keyedBy: A1.Type) in conformance CleanJSONDecoder (in MyProj) :0
15libswiftCore.dylib
dispatch thunk of Decoder.container
(keyedBy: A1.Type) (in libswiftCore.dylib)
1 MyProj
specialized GradeTab.init(from: Decoder) (in MyProj) :0
1 MyProj
protocol witness for Decodable.init(from: Decoder) in conformance GradeTab (in MyProj) :0
18libswiftCore.dylib
dispatch thunk of Decodable.init(from: Decoder) (in libswiftCore.dylib)
1 MyProj
CleanJSONDecoder.unbox(
: Any, as: Decodable.Type) (in MyProj) CleanJSONDecoder+Unbox.swift:475
2 MyProj
CleanJSONDecoder.unbox
(: Any, as: A.Type) (in MyProj) CleanJSONDecoder+Unbox.swift:454
2 MyProj
CleanJSONUnkeyedDecodingContainer.decode
(A.Type) (in MyProj) CleanJSONUnkeyedDecodingContainer.swift:411
2 MyProj
protocol witness for UnkeyedDecodingContainer.decode(A1.Type) in conformance CleanJSONUnkeyedDecodingContainer (in MyProj) :0
23libswiftCore.dylib
Array.init(from: Decoder) (in libswiftCore.dylib)
24libswiftCore.dylib
protocol witness for Decodable.init(from: Decoder) in conformance [A] (in libswiftCore.dylib)
25libswiftCore.dylib
protocol witness for Decodable.init(from: Decoder) in conformance [A] (in libswiftCore.dylib)
26libswiftCore.dylib
dispatch thunk of Decodable.init(from: Decoder) (in libswiftCore.dylib)
2 MyProj
CleanJSONDecoder.unbox(
: Any, as: Decodable.Type) (in MyProj) CleanJSONDecoder+Unbox.swift:475
2 MyProj
CleanJSONDecoder.unbox
(: Any, as: A.Type) (in MyProj) CleanJSONDecoder+Unbox.swift:454
2 MyProj
decodeObject #1
(from: CleanJSONDecoder) in CleanJSONKeyedDecodingContainer.decodeIfPresent(: A1.Type, forKey: A) (in MyProj) CleanJSONKeyedDecodingContainer.swift:886
3 MyProj
protocol witness for KeyedDecodingContainerProtocol.decodeIfPresent(
: A1.Type, forKey: A.Key) in conformance CleanJSONKeyedDecodingContainer (in MyProj) :870
31libswiftCore.dylib
KeyedDecodingContainerBox.decodeIfPresent(: A1.Type, forKey: A.Key) (in libswiftCore.dylib)
32libswiftCore.dylib
KeyedDecodingContainer.decodeIfPresent(
: A1.Type, forKey: A) (in libswiftCore.dylib)
3 MyProj
specialized PARespCourseSelectTabsConfigModel.init(from: Decoder) (in MyProj) :0
3 MyProj
protocol witness for Decodable.init(from: Decoder) in conformance PARespCourseSelectTabsConfigModel (in MyProj) :0
35libswiftCore.dylib
dispatch thunk of Decodable.init(from: Decoder) (in libswiftCore.dylib)
3 MyProj
CleanJSONDecoder.unbox(: Any, as: Decodable.Type) (in MyProj) CleanJSONDecoder+Unbox.swift:475
3 MyProj
CleanJSONDecoder.unbox
(: Any, as: A.Type) (in MyProj) CleanJSONDecoder+Unbox.swift:454
3 MyProj
CleanJSONDecoder.decode
(: A.Type, from: Any) (in MyProj) CleanJSONDecoder.swift:113
3 MyProj
CleanJSONDecoder.decode(
: A.Type, from: JSONContainerConvertible) (in MyProj) CleanJSONDecoder.swift:101
4 MyProj
xxxStruct.transModel(: A.Type, withKeyValues: [String : Any]) (in MyProj) xxx.swift:208

Fails on decoding large Double

This code makes the exception:

let str = "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
let data = str.data(using: .utf8)!
let decoder = CleanJSONDecoder()
do {
  let value = try decoder.decode(Double.self, from: data)
  print("OK = \(value)")
} catch {
  print("Error = \(error)")
}

If you remove one of zeroes in "str" then the decoding will be OK. So the limit is 1e165, after which the decoding fails regardless of Double type can really fit.

Any Plan support Apple Privacy Manifest for cocoapods ?

From Apple docs:

Starting in spring 2024, you must include the privacy manifest for any SDK listed below when you submit new apps in App Store Connect that include those SDKs, or when you submit an app update that adds one of the listed SDKs as part of the update. Signatures are also required in these cases where the listed SDKs are used as binary dependencies.

Could you please add a privacy manifest for this library? See the following URLs for additional information:

https://developer.apple.com/news/?id=r1henawx
https://developer.apple.com/documentation/bundleresources/privacy_manifest_files

请问怎么自定义解析Date类型?

let decoder = CleanJSONDecoder()
decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
    return Date()
})

上面的代码没有生效,看源码里面解析的时候用的是self.options.dateDecodingStrategy,这个在库外面可以配置吗?

中断解析

private func decodeIfNeeded<T : Decodable>(_ type: T.Type, forKey key: Key) throws -> T {
        if let objectValue = try? JSONDecoder().decode(type, from: "{}".data(using: .utf8)!) {
            return objectValue
        } else if let arrayValue = try? JSONDecoder().decode(type, from: "[]".data(using: .utf8)!) {
            return arrayValue
        } else if let stringValue = try decode(String.self, forKey: key) as? T {
            return stringValue
        } else if let boolValue = try decode(Bool.self, forKey: key) as? T {
            return boolValue
        } else if let intValue = try decode(Int.self, forKey: key) as? T {
            return intValue
        } else if let uintValue = try decode(UInt.self, forKey: key) as? T {
            return uintValue
        } else if let doubleValue = try decode(Double.self, forKey: key) as? T {
            return doubleValue
        } else if let floatValue = try decode(Float.self, forKey: key) as? T {
            return floatValue
        }
        let context = DecodingError.Context(codingPath: [key], debugDescription: "Key: <\(key.stringValue)> cannot be decoded")
        throw DecodingError.dataCorrupted(context)
    }

你好,这个方法抛出异常了,抛出异常还是会中断解析吧?

I have been looking for the native JSON processing scheme

In order to achieve the project no invasion,Over the years I have been looking for native JSON processing scheme,until find here。
I based on "Codable" encapsulates a set of treatment scheme,and hope more communication

使用class时,子类Model赋值失败

class father: Codable {
    var id: String
    var newsID: String
    var title: String
    var content: String
    var section: String
    var cat: String
    var publishDatetime: String
    var layout: String

    private enum CodingKeys: String, CodingKey {
        case id
        case newsID
        case title
        case content
        case section
        case cat
        case publishDatetime = "publish_datetime"
        case layout
    }
}

class son: father {

    var landingType: String
    var landingUrl: String

    var cover: String
    var pos: String
    var listPos: String
    var image: String

    private enum CodingKeys: String, CodingKey {
        case landingType = "landing_type"
        case landingUrl = "landing_url"
        case cover
        case pos
        case listPos = "list_pos"
        case image
    }
}
json 

{"data":[{"id":"49","landing_type":"2","landing_url":"http:\/\/hlapp.stheadline.com\/ads_view\/49","title":"test inhouse","content":null,"cover":"49","pos":"2","list_pos":"4","image":null,"layout":"3"},{"id":"47","landing_type":"1","landing_url":"http:\/\/192.168.148.244\/headline_app_back_end\/Ads\/form","title":null,"content":null,"cover":"47","pos":"1","list_pos":"2","image":"hl_app_ads\/2019\/05\/22\/20190522104030_9444.jpg","layout":"1"}],"result":1}

子类中除了父类包含的属性,其他属性都没法赋值

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.