Codable JSON转模型,简单好用又高效,XPath快速JSON子节点定位,二者合二为一,简单高效快捷又好用。
样例模型,Order与Goods都是Codable:
struct Order: Codable {
var id: String?
var money: String?
var goods: [Goods]?
}
struct Goods: Codable {
var id: String?
var name: String?
var price: String?
var count: Int?
}
JSON样例:
{
"msg": "",
"data": {
"orders": [{
"id": "1234",
"money": "65.0",
"goods": [{
"id": "a10",
"price": "25,0",
"name": "重窝小旋风",
"count": 1
}, {
"id": "b11",
"price": "20.0",
"name": "逍遥青",
"count": 2
}]
}, {
"id": "5678",
"money": "708.0",
"goods": [{
"id": "c123",
"price": "708.0",
"name": "一帆精工擎天柱红色纪念款钓台",
"count": 1
}]
}],
"total": 2
},
"code": 0
}
JSON转模型方法:
public func decode<T : Decodable>(_ json : Any) -> T? {
guard JSONSerialization.isValidJSONObject(json) else {
return nil
}
guard let data = try? JSONSerialization.data(withJSONObject: json, options: .init()) else {
return nil
}
let decoder = JSONDecoder()
guard let result = try? decoder.decode(T.self, from: data) else {
return nil
}
return result
}
public func decode<T : Decodable>(_ json : [String: Any], xpath: String) -> T? {
guard let target = json.xpath(xpath) else {
return nil
}
return decode(target)
}
Codable使用:
let data = json["data"] as? [String: Any]
let orders = data?["orders"] as? [[String: Any]] ?? []
let results: [Order] = decode(orders)
XPath+Codable,一行搞定:
let results: [Order] = decode(json, xpath: "data.orders")
结合RxSwift使用样例:
func goodsList(page: Int, keywords: String? = nil, success: (([Goods]) -> ())?, failure: ErrorHandler?) {
provider.rx.request(.goodsList(page: page, keywords: keywords))
.mapCode()
.mapObject([Goods].self, xpath: "data.spus")
.subscribe { goods in
success?(goods)
} onError: { error in
failure?(error)
}
.disposed(by: bag)
}
RxSwift之mapObject:
public extension PrimitiveSequence where Trait == SingleTrait, Element == [String: Any] {
func mapObject<T: Codable>(_ type: T.Type) -> Single<T> {
return flatMap { dict in
guard let t: T = decode(dict) else {
throw YXNetworkError.JsonMap
}
return .just(t)
}
}
func mapObject<T: Codable>(_ type: T.Type, xpath: String) -> Single<T> {
return flatMap { dict in
guard let result = dict.xpath(xpath) else {
throw YXNetworkError.JsonMap
}
guard let t: T = decode(result) else {
throw YXNetworkError.JsonMap
}
return .just(t)
}
}
}
最后附上XPath的实现方法:
public extension Dictionary where Key == String, Value == Any {
func xpath(_ xpath: String) -> Any? {
guard xpath.count > 0 else {
return nil
}
let paths = xpath.components(separatedBy: ".")
var result = self
var correct = true
for i in 0 ..< paths.count {
let path = paths[i]
if i < paths.count - 1 {
if let next = result[path] as? [String: Any] {
result = next
} else {
correct = false
break
}
} else {
if let next = result[path] {
return next
} else {
correct = false
break
}
}
}
guard correct else {
return nil
}
return result
}
}
网友评论