首页 > 解决方案 > JSONDecoder 解码不同类型的相同

问题描述

我正在使用 JSONDecoder 来解析我的 JSON 响应:

{"status":"1","errorCode":"0","msg":"","info":1}

或者

{"status":"1","errorCode":"0","msg":"","info":"a"}

或者

{"status":"1","errorCode":"0","msg":"","info":{"name":"a"}}

或者

{"status":"1","errorCode":"0","msg":"","info":{"text":"b"}}

或者

{"status":"1","errorCode":"0","msg":"","info":[{"name":"a"}]}

这是我的basejson

import Foundation

class BaseJson: Decodable {
    var id = UUID()
    var status: String
    var errorCode: String
    var msg: String

    private enum CodingKeys: String, CodingKey {
        case status = "status", errorCode = "errorCode", msg = "msg", info = "info"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        status = try container.decodeIfPresent(String.self, forKey: .status) ?? "0"
        errorCode = try container.decodeIfPresent(String.self, forKey: .errorCode) ?? "0"
        msg = try container.decodeIfPresent(String.self, forKey: .msg) ?? "1"
    }
}

那么我应该怎么做才能让这个基本类解析所有结果

标签: iosjson

解决方案


您可以使用具有关联值的 Enum 来处理它。

但是正如 vadian 所说,如果可以的话,API 最好根据相同的参数始终响应相同类型的数据。

你可以添加这个:

enum Info: Decodable {

    enum DecodingError: Error {
        case unknownValueType
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let asInt = try? container.decode(Int.self) {
            self = .intValue(asInt)
        } else if let asString = try? container.decode(String.self) {
            self = .stringValue(asString)
        } else if let asDict = try? container.decode([String: String].self) {
            self = .dictValue(asDict)
        } else if let asArrayofDicts = try? container.decode([[String: String]].self) {
            self = .arrayOfDictValue(asArrayofDicts)
        } else {
            throw Info.DecodingError.unknownValueType
        }
    }

    case intValue(Int)
    case stringValue(String)
    case dictValue([String: String])
    case arrayOfDictValue([[String: String]])
}

我使用[String: String]and [[String: String]],但如果有限制,您也可以放置自己的自定义结构:

struct InfoText:可解码 { 让文本:字符串 }

结构信息名称:可解码 { 让名称:字符串 }

并做:

案例信息文本(信息文本)

如果让 asInfoText = 试试?container.decode(InfoText.self) { ... }

等等

完整的代码和文本(在 Plyagrounds 中复制粘贴):

class BaseJson: Decodable {

    enum Info: Decodable {

        enum DecodingError: Error {
            case unknownValueType
        }

        init(from decoder: Decoder) throws {
            let container = try decoder.singleValueContainer()
            if let asInt = try? container.decode(Int.self) {
                self = .intValue(asInt)
            } else if let asString = try? container.decode(String.self) {
                self = .stringValue(asString)
            } else if let asDict = try? container.decode([String: String].self) {
                self = .dictValue(asDict)
            } else if let asArrayofDicts = try? container.decode([[String: String]].self) {
                self = .arrayOfDictValue(asArrayofDicts)
            } else {
                throw Info.DecodingError.unknownValueType
            }
        }

        case intValue(Int)
        case stringValue(String)
        case dictValue([String: String])
        case arrayOfDictValue([[String: String]])
    }

    var id = UUID()
    var status: String
    var errorCode: String
    var msg: String
    var info: Info

    private enum CodingKeys: String, CodingKey {
        case status = "status", errorCode = "errorCode", msg = "msg", info = "info"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        status = try container.decode(String.self, forKey: .status)
        errorCode = try container.decode(String.self, forKey: .errorCode)
        msg = try container.decode(String.self, forKey: .msg)
        info = try container.decode(Info.self, forKey: .info)
    }

    static func test() {
        let jsonStr = """
[
{"status":"1","errorCode":"0","msg":"","info":1},
{"status":"1","errorCode":"0","msg":"","info":"a"},
{"status":"1","errorCode":"0","msg":"","info":{"name":"a"}},
{"status":"1","errorCode":"0","msg":"","info":{"text":"b"}},
{"status":"1","errorCode":"0","msg":"","info":[{"name":"a"}]}
]
"""

        let jsonData = jsonStr.data(using: .utf8)!

        do {
            let bases = try JSONDecoder().decode([BaseJson].self, from: jsonData)
            print(bases)

            for aBase in bases {
                var infoStr = ""
                switch aBase.info {
                case .arrayOfDictValue(let array):
                    infoStr = "\(array)"
                case .dictValue(let dictt):
                    infoStr = "\(dictt)"
                case .intValue(let intV):
                    infoStr = "\(intV)"
                case .stringValue(let str):
                    infoStr = str
                }
                print("Got: \(aBase.id), status: \(aBase.status), errorCode: \(aBase.errorCode), msg: \(aBase.msg), info: \(aBase.info) - badStringConversion for understanding: \(infoStr)")
            }
        } catch {
            print("Error: \(error)")
        }
    }
}

BaseJson.test()

输出:

$> [__lldb_expr_75.BaseJson, __lldb_expr_75.BaseJson, __lldb_expr_75.BaseJson, __lldb_expr_75.BaseJson, __lldb_expr_75.BaseJson]
$> Got: C92A4315-413C-4DA3-9D0B-E6CE3B081AF3, status: 1, errorCode: 0, msg: , info: intValue(1) - badStringConversion for understanding: 1
$> Got: 1AB9FE74-E282-4AC9-97B1-814DD5C9F412, status: 1, errorCode: 0, msg: , info: stringValue("a") - badStringConversion for understanding: a
$> Got: F082FD0D-818D-4A6F-906A-3A80D911C2BA, status: 1, errorCode: 0, msg: , info: dictValue(["name": "a"]) - badStringConversion for understanding: ["name": "a"]
$> Got: 2424AFD2-8417-4FCB-B97A-D03AB30A9C45, status: 1, errorCode: 0, msg: , info: dictValue(["text": "b"]) - badStringConversion for understanding: ["text": "b"]
$> Got: 05DDB7CB-DD0E-41BB-B878-F1AEBF0D6F43, status: 1, errorCode: 0, msg: , info: arrayOfDictValue([["name": "a"]]) - badStringConversion for understanding: [["name": "a"]]

推荐阅读