在swift 4中解码动态JSON结构

在swift 4中解码动态JSON结构,swift,decodable,Swift,Decodable,我有以下问题,我不知道如何处理 我的JSON响应可以如下所示: { "data": { "id": 7, "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDY1MTU0NDMsImRhdGEiOiJ2bGFkVGVzdCIsImlhdCI6MTU0NjUwODI0M30.uwuPhlnchgBG4E8IvHvK4bB1Yj-TNDgmi7wUAiKmoVo" }, "error": n

我有以下问题,我不知道如何处理

我的
JSON
响应可以如下所示:

{ 
  "data": {
      "id": 7,
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDY1MTU0NDMsImRhdGEiOiJ2bGFkVGVzdCIsImlhdCI6MTU0NjUwODI0M30.uwuPhlnchgBG4E8IvHvK4bB1Yj-TNDgmi7wUAiKmoVo"
   },
  "error": null
}
{
 "data": [{
     "id": 12
    }, {
      "id": 2
    }, {
       "id": 5
    }, {
       "id": 7
    }],
 "error": null
}
或者像这样:

{ 
  "data": {
      "id": 7,
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDY1MTU0NDMsImRhdGEiOiJ2bGFkVGVzdCIsImlhdCI6MTU0NjUwODI0M30.uwuPhlnchgBG4E8IvHvK4bB1Yj-TNDgmi7wUAiKmoVo"
   },
  "error": null
}
{
 "data": [{
     "id": 12
    }, {
      "id": 2
    }, {
       "id": 5
    }, {
       "id": 7
    }],
 "error": null
}
因此,简而言之,数据可以是单个对象,也可以是阵列。我所拥有的是:

struct ApiData: Decodable {
    var data: DataObject?
    var error: String?
}

struct DataObject: Decodable {
    var userId: Int?

    enum CodingKeys: String, CodingKey {
        case userId = "id"
    }
}
这在第一个用例中可以很好地工作,但一旦数据转换为

var数据:[DataObject?]

如何在不复制代码的情况下使其动态化

编辑:这也是我解码对象的方式

 func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(_ apiData: ApiData?) -> ()) {
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    session.dataTask(with: urlRequest) {
        (data, response, error) in
        guard let _ = response, let data = data else {return}

        if let responseCode = response as? HTTPURLResponse {
            print("Response has status code: \(responseCode.statusCode)")
        }

        do {
            let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data)
            completion(retreived)
        } catch let decodeError as NSError {
            print("Decoder error: \(decodeError.localizedDescription)\n")
            return
        }
        }.resume()
}

如果您的数据只有两种可能的结果,一种选择是尝试将数据解析为预期的类型之一,如果失败,您知道数据属于其他类型,然后可以相应地处理它


请参见

如果您的数据只有两种可能的结果,一种选择是尝试将数据解析为预期的类型之一,如果失败,您知道数据属于其他类型,然后可以相应地处理它

请参见,您可以尝试

struct Root: Codable {
    let data: DataUnion
    let error: String?
}

enum DataUnion: Codable {
    case dataClass(DataClass)
    case datumArray([Datum])

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode([Datum].self) {
            self = .datumArray(x)
            return
        }
        if let x = try? container.decode(DataClass.self) {
            self = .dataClass(x)
            return
        }
        throw DecodingError.typeMismatch(DataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for DataUnion"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .dataClass(let x):
            try container.encode(x)
        case .datumArray(let x):
            try container.encode(x)
        }
    }
}

struct Datum: Codable {
    let id: Int
}

struct DataClass: Codable {
    let id: Int
    let token: String
}

你可以试试

struct Root: Codable {
    let data: DataUnion
    let error: String?
}

enum DataUnion: Codable {
    case dataClass(DataClass)
    case datumArray([Datum])

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode([Datum].self) {
            self = .datumArray(x)
            return
        }
        if let x = try? container.decode(DataClass.self) {
            self = .dataClass(x)
            return
        }
        throw DecodingError.typeMismatch(DataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for DataUnion"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .dataClass(let x):
            try container.encode(x)
        case .datumArray(let x):
            try container.encode(x)
        }
    }
}

struct Datum: Codable {
    let id: Int
}

struct DataClass: Codable {
    let id: Int
    let token: String
}


如果
数据
可以是单个对象或数组,请编写一个自定义初始值设定项,首先对数组进行解码,如果发生类型不匹配错误,请对单个对象进行解码<代码>数据仍然声明为数组

由于
token
仅出现在单个对象中,因此属性声明为可选

struct ApiData: Decodable {
    let data : [DataObject]
    let error : String?

    private enum CodingKeys : String, CodingKey { case data, error }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        do {
            data = try container.decode([DataObject].self, forKey: .data)
        } catch DecodingError.typeMismatch {
            data = [try container.decode(DataObject.self, forKey: .data)]
        }
        error = try container.decodeIfPresent(String.self, forKey: .error)
    }
}


struct DataObject: Decodable {
    let userId : Int
    let token : String?

    private enum CodingKeys: String, CodingKey { case userId = "id", token }
}
编辑:可以改进接收数据的代码。您应该添加更好的错误处理,以返回所有可能的错误:

func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(ApiData?, Error?) -> Void) {
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    session.dataTask(with: urlRequest) {
        (data, response, error) in
        if let error = error { completion(nil, error); return }

        if let responseCode = response as? HTTPURLResponse {
            print("Response has status code: \(responseCode.statusCode)")
        }

        do {
            let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data!)
            completion(retreived, nil)
        } catch {
            print("Decoder error: ", error)
            completion(nil, error)
        }
        }.resume()
}

如果
数据
可以是单个对象或数组,请编写一个自定义初始值设定项,首先对数组进行解码,如果发生类型不匹配错误,请对单个对象进行解码<代码>数据仍然声明为数组

由于
token
仅出现在单个对象中,因此属性声明为可选

struct ApiData: Decodable {
    let data : [DataObject]
    let error : String?

    private enum CodingKeys : String, CodingKey { case data, error }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        do {
            data = try container.decode([DataObject].self, forKey: .data)
        } catch DecodingError.typeMismatch {
            data = [try container.decode(DataObject.self, forKey: .data)]
        }
        error = try container.decodeIfPresent(String.self, forKey: .error)
    }
}


struct DataObject: Decodable {
    let userId : Int
    let token : String?

    private enum CodingKeys: String, CodingKey { case userId = "id", token }
}
编辑:可以改进接收数据的代码。您应该添加更好的错误处理,以返回所有可能的错误:

func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(ApiData?, Error?) -> Void) {
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    session.dataTask(with: urlRequest) {
        (data, response, error) in
        if let error = error { completion(nil, error); return }

        if let responseCode = response as? HTTPURLResponse {
            print("Response has status code: \(responseCode.statusCode)")
        }

        do {
            let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data!)
            completion(retreived, nil)
        } catch {
            print("Decoder error: ", error)
            completion(nil, error)
        }
        }.resume()
}

使用
generic
的强大功能,它简单如下:

struct ApiData<T: Decodable>: Decodable {
    var data: T?
    var error: String?
}

struct DataObject: Decodable {
    private var id: Int?

    var userId:Int? {
        return id
    }
}
struct ApiData:可解码{
var数据:T?
变量错误:字符串?
}
结构数据对象:可解码{
私有变量id:Int?
var userId:Int{
返回id
}
}
使用

if let obj = try? NetworkManager.shared.decoder.decode(ApiData<DataObject>.self, from: data) {
    //Do somthing
} else if let array = try NetworkManager.shared.decoder.decode(ApiData<[DataObject]>.self, from: data) {
    // Do somthing
}
如果让obj=try?NetworkManager.shared.decoder.decode(ApiData.self,from:data){
//干坏事
}如果let array=try NetworkManager.shared.decoder.decode(ApiData.self,from:data),则为else{
//干坏事
}

使用
通用的功能,简单如下:

struct ApiData<T: Decodable>: Decodable {
    var data: T?
    var error: String?
}

struct DataObject: Decodable {
    private var id: Int?

    var userId:Int? {
        return id
    }
}
struct ApiData:可解码{
var数据:T?
变量错误:字符串?
}
结构数据对象:可解码{
私有变量id:Int?
var userId:Int{
返回id
}
}
使用

if let obj = try? NetworkManager.shared.decoder.decode(ApiData<DataObject>.self, from: data) {
    //Do somthing
} else if let array = try NetworkManager.shared.decoder.decode(ApiData<[DataObject]>.self, from: data) {
    // Do somthing
}
如果让obj=try?NetworkManager.shared.decoder.decode(ApiData.self,from:data){
//干坏事
}如果let array=try NetworkManager.shared.decoder.decode(ApiData.self,from:data),则为else{
//干坏事
}


如果出现
错误,API会发送什么!=null
?@vadian它发送一个字符串,你可以在ApiData结构中看到它。@RobertDresler我不确定我是否理解这个问题?借助于符合可解码protocol@RobertDresler我编辑了这篇文章来回答你的问题。使用genericAnd的功能,如果出现
错误,API会发送什么消息!=null
?@vadian它发送一个字符串,你可以在ApiData结构中看到它。@RobertDresler我不确定我是否理解这个问题?借助于符合可解码protocol@RobertDresler我编辑了这篇文章来回答你的问题。使用genericYes的强大功能,但这会复制代码,两种类型都有var userId:Int?还有一堆我在上面的例子中没有粘贴的其他用户信息。是的,但这会重复代码,两种类型都有var userId:Int?还有一堆我在上面的例子中没有粘贴的其他用户信息。谢谢,但这并不能避免重复。structs Datum和DataClass都有let id。您可以将它们设为1,但如果您需要令牌,则应将令牌设为可选,但这并不能避免重复。structs Datum和DataClass都有let id。您可以将它们设为1,但如果您希望,则应将令牌设为可选。这是我正在查找的,不知道DecodingError.typeMismatch。非常感谢。当涉及到可编码时,你们总是能找到答案。这就是我一直在寻找的,不知道DecodingError.typeMismatch。非常感谢。当涉及到可编码的问题时,你总是能找到答案