简介
swift中,主流的json转模型有两种:原生实现(Decodable)与HandyJSON
本人之前HandyJSON,后Xcode升级10.0之后,项目报语法错误,官网需要等苹果给出明确写法之后才能更新,等不了,还原生实现。 *HandyJSON官方文档上有很多教程方法介绍,简单易用,这里不过多介绍。
原生实现(Decodable)
1、创建结构体
需要注意:
*不能确定这些字断有值的,需要加可选,不然解析不成功
*对映json中字断的key值 如果都是一样 可以不写,不一样如下
/**
不能确定这些字断有值的,需要加可选,不然解析不成功
*/
public struct TestModel:Decodable{
var user:String
var id:String
var model:String?
///对映json中字断的key值 如果都是一样 可以不写
enum CodingKeys:String,CodingKey {
case id = "deviceId"
case user = "name"
case model
}
}
创建一个Geojson文件内容如下:
[{
"deviceId": "G7594S992Q",
"name": "1111111111111111111111111111111",
"model": "iPhone X",
}, {
"deviceId": "9D632SMR8G",
"name": "12345",
"model": "iPhone 7 Plus",
}]
这里需要注意下:创建的json文件需要添加到 Copy Bundle resources中,不然 Bundle获取不到文件。方法:Targets->Build Phases ->Copy Bundle resources
func testJson(){
guard let url = Bundle.main.path(forResource: "jsonData", ofType: "geojson") else {
print("url 没有数据")//如果没有取到,按照上面步骤验查一下。
return
}
let data = try? Data(contentsOf: URL(fileURLWithPath: url), options: Data.ReadingOptions.alwaysMapped)
guard let arr = try? JSONDecoder().decode([TestModel].self, from: data!) else {
print("arr 没有数据")
return
}
print("arr =\(arr)")
}
执行结果:
arr =[test1.testModel(user: "1111111111111111111111111111111", id: "G7594S992Q", model: Optional("iPhone X")), test1.testModel(user: "12345", id: "9D632SMR8G", model: Optional("iPhone 7 Plus"))]
如果获取的数据是字典格式
try? JSONDecoder().decode(TestModel.self, from: data!)
如果是字典中嵌套需要取得值,此处可以灵活运用。
try? JSONDecoder().decode([String:TestModel].self, from: data!)
也可以是同类型的数组或者字典
try? JSONDecoder().decode([String:[String:String]].self, from: data!)
注意:拿到的data必须要转成 Data.ReadingOptions.alwaysMapped格式
简单封装
public extension Encodable {
/// codable 转换json字符串
/// - Returns: json
func toJSONString() -> String? {
guard let data = try? JSONEncoder().encode(self) else {
return nil
}
return String(data: data, encoding: .utf8)
}
/// 对象转换为字典或者数组
/// - Returns: dict/array
func toJSONObject() -> Any? {
guard let data = try? JSONEncoder().encode(self) else {
return nil
}
return try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
}
}
public extension Decodable {
//
static func decodeJSON(from string: String?, designatedPath: String? = nil) -> Self? {
guard let data = string?.data(using: .utf8),
let jsonData = getInnerObject(inside: data, by: designatedPath)
else {
return nil
}
do {
return try JSONDecoder().decode(Self.self, from: jsonData)
} catch {
DLog(error)
print("\n:jsonData=\(String(describing: string))")
return nil
}
}
/// 字典或数组转换模型或者数组模型
/// - Parameters:
/// - jsonObject: 传入数据类型
/// - designatedPath:
/// - Returns:
static func decodeJSON(from jsonObject: Any?, designatedPath: String? = nil) -> Self? {
guard let jsonObject = jsonObject,
JSONSerialization.isValidJSONObject(jsonObject),
let data = try? JSONSerialization.data(withJSONObject: jsonObject, options: []),
let jsonData = getInnerObject(inside: data, by: designatedPath)
else {
return nil
}
do {
return try JSONDecoder().decode(Self.self, from: jsonData)
} catch {
DLog(error)
print("\n:jsonData=\(jsonObject)")
return nil
}
}
}
// 扩展Array,添加将jsonString或者jsonObject解码到对应对象数组的方法
public extension Array where Element: Codable {
static func decodeJSON(from jsonString: String?, designatedPath: String? = nil) -> [Element?]? {
guard let data = jsonString?.data(using: .utf8),
let jsonData = getInnerObject(inside: data, by: designatedPath),
let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) as? [Any]
else {
return nil
}
return Array.decodeJSON(from: jsonObject)
}
static func decodeJSON(from array: [Any]?) -> [Element?]? {
return array?.map({ Element.decodeJSON(from: $0) })
}
}
/// - Parameters:
/// - jsonData: json data
/// - designatedPath: 获取json object中指定路径
/// - Returns: 可能是json object
fileprivate func getInnerObject(inside jsonData: Data?, by designatedPath: String?) -> Data? {
// 保证jsonData不为空,designatedPath有效
guard let _jsonData = jsonData,
let paths = designatedPath?.components(separatedBy: "."),
paths.count > 0
else {
return jsonData
}
// 从jsonObject中取出designatedPath指定的jsonObject
var jsonObject = try? JSONSerialization.jsonObject(with: _jsonData, options: .allowFragments)
if let arr = jsonObject as? [[String: Any]], arr.count == 1 {
jsonObject = arr.first
}
var result: Any? = jsonObject
var abort = false
var next = jsonObject as? [String: Any]
paths.forEach({ seg in
if seg.trimmingCharacters(in: .whitespacesAndNewlines) == "" || abort {
return
}
if let _next = next?[seg] {
result = _next
next = _next as? [String: Any]
} else {
abort = true
}
})
// 判断条件保证返回正确结果,保证没有流产,保证jsonObject转换成了Data类型
guard abort == false,
let resultJsonObject = result,
let data = try? JSONSerialization.data(withJSONObject: resultJsonObject, options: [])
else {
return nil
}
return data
}
/// json 中取值
/// - Parameters:
/// - json: json string
/// - designatedPath: key
/// - Returns: 获取到的字符串
func getInnerJsongString(inside json: String?, by designatedPath: String?) -> String? {
// 保证jsonData不为空,designatedPath有效
guard let js = json, let _jsonData = js.data(using: .utf8),
let paths = designatedPath?.components(separatedBy: "."),
paths.count > 0
else {
return json
}
// 从jsonObject中取出designatedPath指定的jsonObject
var jsonObject = try? JSONSerialization.jsonObject(with: _jsonData, options: .allowFragments)
if let arr = jsonObject as? [[String: Any]], arr.count == 1 {
jsonObject = arr.first
}
var result: Any? = jsonObject
var abort = false
var next = jsonObject as? [String: Any]
paths.forEach({ seg in
if seg.trimmingCharacters(in: .whitespacesAndNewlines) == "" || abort {
return
}
if let _next = next?[seg] {
result = _next
next = _next as? [String: Any]
} else {
abort = true
}
})
if !abort, let str = result as? String {
return str
}
if !abort, let str = result as? Int {
return "\(str)"
}
// 判断条件保证返回正确结果,保证没有流产,保证jsonObject转换成了Data类型
guard abort == false,
let resultJsonObject = result,
let data = try? JSONSerialization.data(withJSONObject: resultJsonObject, options: [])
else {
return nil
}
return String(data: data, encoding: .utf8)
}
extension Dictionary {
/// 根据key 取值
/// - Parameter key: key
/// - Returns: 输出字符串
func key(_ key: String) -> String {
let value = (self as? [String: Any])? [key]
if let num = value as? Int {
return "\(num)"
}
return value as? String ?? ""
}
/// 字典转换为data
/// - Returns: data
func toData() -> Data? {
if !JSONSerialization.isValidJSONObject(self) {
return nil
}
do {
let data = try JSONSerialization.data(withJSONObject: self, options: [])
return data
} catch let error {
DLog(error.localizedDescription)
return nil
}
}
func tojson() -> String {
return getJSONString(dictionary: self)
}
}
extension Array {
func tojson() -> String {
return getJSONString(dictionary: self)
}
}
更新 解决类型不一致问题
extension KeyedDecodingContainer {
public func decodeIfPresent(_ type: String.Type, forKey key: K) throws -> String? {
if let value = try? decode(type, forKey: key) {
return value
}
if let value = try? decode(Int.self, forKey: key) {
return String(value)
}
if let value = try? decode(Float.self, forKey: key) {
return String(value)
}
return nil
}
public func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? {
if let value = try? decode(type, forKey: key) {
return value
}
if let value = try? decode(String.self, forKey: key) {
return Int(value)
}
return nil
}
public func decodeIfPresent(_ type: Float.Type, forKey key: K) throws -> Float? {
if let value = try? decode(type, forKey: key) {
return value
}
if let value = try? decode(String.self, forKey: key) {
return Float(value)
}
return nil
}
public func decodeIfPresent(_ type: Bool.Type, forKey key: K) throws -> Bool? {
if let value = try? decode(type, forKey: key) {
return value
}
if let value = try? decode(String.self, forKey: key) {
if let valueInt = Int(value) {
return Bool(valueInt != 0)
}
return nil
}
if let value = try? decode(Int.self, forKey: key) {
return Bool(value != 0)
}
return nil
}
public func decodeIfPresent(_ type: Double.Type, forKey key: K) throws -> Double? {
if let value = try? decode(type, forKey: key) {
return value
}
if let value = try? decode(String.self, forKey: key) {
return Double(value)
}
return nil
}
public func decodeIfPresent<T>(_ type: T.Type, forKey key: K) throws -> T? where T: Decodable {
return try? decode(type, forKey: key)
}
}
用法
///model获取
let mode = TestModel.decodeJSON(from:"JSON 字符串")
///数组获取
let list = [TestModel].decodeJSON(from:"JSON 字符串")