web-dev-qa-db-fra.com

Décoder un JSON sans clé dans Swift 4

J'utilise une API qui renvoie ce JSON assez horrible:

[
  "A string",
  [
    "A string",
    "A string",
    "A string",
    "A string",
    …
  ]
]

J'essaie de décoder le tableau imbriqué en utilisant JSONDecoder, mais il n'a pas de clé unique et je ne sais vraiment pas par où commencer… Avez-vous une idée?

Merci beaucoup!

6
davidg

Si la structure reste la même, vous pouvez utiliser cette approche décodable .

Commencez par créer un modèle décodable comme ceci:

struct MyModel: Decodable {
    let firstString: String
    let stringArray: [String]

    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        firstString = try container.decode(String.self)
        stringArray = try container.decode([String].self)
    }
}

Ou si vous voulez vraiment conserver la structure du JSON, comme ceci:

struct MyModel: Decodable {
    let array: [Any]

    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        let firstString = try container.decode(String.self)
        let stringArray = try container.decode([String].self)
        array = [firstString, stringArray]
    }
}

Et l'utiliser comme ça

let jsonString = """
["A string1", ["A string2", "A string3", "A string4", "A string5"]]
"""
if let jsonData = jsonString.data(using: .utf8) {
    let myModel = try? JSONDecoder().decode(MyModel.self, from: jsonData)
}
2
fl034

C'est un peu intéressant pour le décodage.

Vous n'avez aucune key. Donc, cela élimine le besoin d'un wrapper struct.

Mais regardons les types intérieurs. Vous obtenez une combinaison de types String et [String]. Vous avez donc besoin de quelque chose qui traite de ce type de mélange. Vous auriez besoin d'une enum pour être précis.

// I've provided the Encodable & Decodable both with Codable for clarity. You obviously can omit the implementation for Encodable
enum StringOrArrayType: Codable {
    case string(String)
    case array([String])

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            self = try .string(container.decode(String.self))
        } catch DecodingError.typeMismatch {
            do {
                self = try .array(container.decode([String].self))
            } catch DecodingError.typeMismatch {
                throw DecodingError.typeMismatch(StringOrArrayType.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 .string(let string):
            try container.encode(string)
        case .array(let array):
            try container.encode(array)
        }
    }
}

Processus de décodage:

let json = """
[
  "A string",
  [
    "A string",
    "A string",
    "A string",
    "A string"
  ]
]
""".data(using: .utf8)!

do {
    let response = try JSONDecoder().decode([StringOrArrayType].self, from: json)
    // Here, you have your Array
    print(response) // ["A string", ["A string", "A string", "A string", "A string"]]

    // If you want to get elements from this Array, you might do something like below
    response.forEach({ (element) in
        if case .string(let string) = element {
            print(string) // "A string"
        }
        if case .array(let array) = element {
            print(array) // ["A string", "A string", "A string", "A string"]
        }
    })
} catch {
    print(error)
}
2
nayem

Une solution possible consiste à utiliser la variable JSONSerialization. Vous pouvez alors simplement creuser à l'intérieur d'un tel fichier json, en procédant ainsi:

import Foundation

let jsonString = "[\"A string\",[\"A string\",\"A string\", \"A string\", \"A string\"]]"
if let jsonData = jsonString.data(using: .utf8) {
    if let jsonArray = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [Any] {
        jsonArray.forEach {
            if let innerArray = $0 as? [Any] {
                print(innerArray) // this is the stuff you need
            }
        }
    }
}
1
Andrea Mugnaini