Swift Codable:处理Optimized JSON生成类型Mismatch

时间:2018-03-12 16:38:10

标签: swift codable

(以下英文翻译)

J'essaie de trouver la bonne ou lameilleurefaçondetraiter les APIs qui envient retourneuneréponseJSONoptimisé。

Si vous n'avez pasdejareçucegenre de JSONcommeréponsed'unserveur,voici un peu d'information。 Si vous recevezuneréponse倒入代码#Couleur en hexadecimal,例如:倒入代码FF2233。 le serveur envoie {“color”:“FF2233”},mais si le code est 223344,le serveur envoie {“color”:223344}àlaplace,en supprimant les guillemets。同性恋倾诉un seul tableau d'objets - il envoie l'objet sans les crochets。 Ceci aide a baiser le consommation de bande passante si vous avez un service webquireçoisbeaucoupderequêtes。

Jusqu'àprésent,Jusque a prenant sous un Playground a corriger le l'erreur en utilisant un Try / catch .. mais ceci ne me semblepasêtrelabonnefaçondede faire face aceprollème,car je vais devoir faire cette correction pour chaque typedeRéponse。

struct Test : Codable {

    var a : String?

    enum CodingKeys: String, CodingKey {
        case a = "a"
    }
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        do{
            a = try values.decodeIfPresent(String.self, forKey: .a)
        }
        catch{
           a = String(describing: try values.decodeIfPresent(Int.self, forKey: .a)!)
        }
    }

    init(a: String)
    {
        self.a = a
    }
}

var a = Test(a: "FF2233")
var jd = try? JSONEncoder().encode(a)
var jt = String(bytes: jd!, encoding: .utf8)
jt = "{\"a\":223365}"
jd = jt?.data(using: .utf8)
do{
    let res = try JSONDecoder().decode(Test.self, from: jd!)
    print(res.a!)
} catch{
    print(error)
}

Existe-t-il un moyendevérifierletype d'une valeur avant de tenter deladédininafin que nous puissions au moins essayer de l'ui donner le bon type?

我试图找出处理发送优化JSON的API的正确或最佳方法。 如果你没有将优化的JSON作为服务器响应,这里有一些背景知识,比如你对#color代码的响应为#FF2233服务器发出{“color”:“FF2233”}, 但如果代码是223344,那么服务器会发出{“color”:223344}删除引号 对于单个对象数组,它使用括号发送对象 到目前为止,这是我在操场上作为测试的...它可以工作,但在我看来,这不是解决我想要解决的问题的最好方法。

有没有办法在尝试解码之前检查值的类型,以便我们至少可以尝试正确地转换它?

2 个答案:

答案 0 :(得分:1)

这里你要处理的是严格破解的JSON。你的方法是必要的。这是愚蠢的,但这只是必要的,因为有人作出了一个愚蠢的决定,向你发送破碎的JSON。

您可以通过扩展KeyedDecodingContainer来稍微清理代码,以便在名为lenientDecode()的方法中包含这些更改。然后你可以写a = values.lenientDecode(String.self, forKey:.a)之类的东西。您仍然会进行相同的检查,但将它们放在一个单独的方法中可以更容易地重复对多个字段的检查。

答案 1 :(得分:1)

我在尝试处理您的数据类型时发现它非常有趣。

首先让它分解为基础类型。在最低级别,您有IntString。然后你有Single个对象或Array。在最高级别,您应该有struct可以处理 Root 对象。基本上你需要两个enum来包裹你的struct。试试吧:

enum IntOrString: Codable {
    case int(Int)
    case string(String)
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            self = try .int(container.decode(Int.self))
        } catch DecodingError.typeMismatch {
            do {
                self = try .string(container.decode(String.self))
            } catch DecodingError.typeMismatch {
                throw DecodingError.typeMismatch(IntOrString.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Encoded payload conflicts with expected type, (Int or String)"))
            }
        }
    }
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .int(let int):
            try container.encode(int)
        case .string(let string):
            try container.encode(string)
        }
    }
}

enum SingleOrArray: Codable {
    case single(IntOrString)
    case array([IntOrString])
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            self = try .single(container.decode(IntOrString.self))
        } catch DecodingError.typeMismatch {
            do {
                self = try .array(container.decode([IntOrString].self))
            } catch DecodingError.typeMismatch {
                throw DecodingError.typeMismatch(SingleOrArray.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Encoded payload conflicts with expected type"))
            }
        }
    }
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .single(let single):
            try container.encode(single)
        case .array(let array):
            try container.encode(array)
        }
    }
}

struct Root: Codable {
    let color: SingleOrArray
}

解码过程

let jsonData = """
{
 "color":["FF2233", "FF2234", "FF2235"]
}
""".data(using: .utf8)!
do {
    let response = try JSONDecoder().decode(Root.self, from: jsonData)
    //print(response)    //["FF2233", "FF2234", "FF2235"]

    // If you want to get underlying elements from this structure, you might do something like below
    // This is your single object
    if case .single(let single) = response.color {
        // Every single object may be an Int or a String
        if case .int(let int) = single {
            print(int)
        }
        if case .string(let string) = single {
            print(string)
        }
    }

    // This is your array
    if case .array(let array) = response.color {
        array.forEach({ (element) in
            // Each element of your array may be an Int or a String
            if case .int(let int) = element {
                print(int)
            }
            if case .string(let string) = element {
                print(string)
            }
        })
    }
} catch {
    print(error)
}

这可以处理的数据类型是:

您可以尝试使用其中一个对象替换上述JSON中的键color的值

  1. String "FF2233"
  2. Int 223344
  3. [String] ["FF2233", "FF2234", "FF2235"]
  4. [Int] [223344, 223345, 223346]
  5. String / Int mixed数组["FF2233", "FF2234", 223344, 223345, "FF2235", 223346]
  6.   

    关于此设计最令人惊讶的事实是您可以解析[Int & String]

    的混合