2
{
"values":[
[1,1,7,"Azuan Child","Anak Azuan","12345","ACTIVE","Morning",7,12,"2017-11-09 19:45:00"],
[28,1,0,"Azuan Child2","Amran","123456","ACTIVE","Evening",1,29,"2017-11-09 19:45:00"]
]
}

わかりました、これはサーバーから受け取った私のjson形式です

今、私はそれを自分の構造体にデコードしたいのですが、まだ運がありません。

struct ChildrenTable: Decodable {
    var values: [[String]]?
}

そして、URLSession の呼び出し元メソッドは次のようになります

URLSession.shared.dataTask(with: request) { (data, response, err) in
        guard let data = data else { return }

        let dataAsString = String(data: data, encoding: .utf8)
        print(dataAsString)

        do {
            let children  = try
                JSONDecoder().decode(ChildrenTable.self, from: data)
                print (children)
        } catch let jsonErr {
            print ("Error serializing json: ", jsonErr)
        }
    }.resume()

そして、私が得たエラーは

Error serializing json:  
typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [Vito_Parent.ChildrenTable.(CodingKeys in _1B826CD7D9609504747BED0EC0B7D3B5).values, Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 0", intValue: Optional(0)), 
Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 0", intValue: Optional(0))], 
debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))

配列に int があることはわかっており、値に String をキャストするだけですvar values: [[String]]?(このエラー ポップアップの理由)。ただし、Decodable のプロトコルに従っているため、構造体で多次元配列やタプルを使用することはできません。

また、「辞書をデコードすると予想されますが、代わりに配列が見つかりました」というエラーがスローされるため、データを辞書に変換することもできません。

この問題を解決するためのアイデアはありますか? データに文字列型をキャストしようとしましたが、まだうまくいきません...

p/s: すべての json 形式が文字列型であれば問題ありませんが、API から呼び出すため、それを変更する権限がありません。

4

5 に答える 5

5

As you said, your json array is multi-type but you are trying to decode all into String. Default conformance of String to Decodable does not allow that. The only solution comes into my mind is to introduce new type.

struct IntegerOrString: Decodable {
    var value: Any

    init(from decoder: Decoder) throws {
        if let int = try? Int(from: decoder) {
            value = int
            return
        }

        value = try String(from: decoder)
    }
}

struct ChildrenTable: Decodable {
    var values: [[IntegerOrString]]?
}

Run online

于 2017-11-10T04:11:28.347 に答える
2

この回答は、@ Orkhan Alikhanovによる回答の上に構築されています

値はIntまたはStringであるため、 の代わりに列挙型を使用してより適切に表すことができますAny

次のコードを Playground に貼り付けることができます

JSON

それでは、JSONから始めましょう

let data = """
{
    "values": [
        [1, 1, 7, "Azuan Child", "Anak Azuan", "12345", "ACTIVE", "Morning", 7, 12, "2017-11-09 19:45:00"],
        [28, 1, 0, "Azuan Child2", "Amran", "123456", "ACTIVE", "Evening", 1, 29, "2017-11-09 19:45:00"]
    ]
}
""".data(using: .utf8)!

データ・モデル

これで、モデルを定義できます (これは になりますDecodable)

enum IntOrString: Decodable {

    case int(Int)
    case string(String)

    init(from decoder: Decoder) throws {

        if let string = try? decoder.singleValueContainer().decode(String.self) {
            self = .string(string)
            return
        }

        if let int = try? decoder.singleValueContainer().decode(Int.self) {
            self = .int(int)
            return
        }

        throw IntOrStringError.intOrStringNotFound
    }

    enum IntOrStringError: Error {
        case intOrStringNotFound
    }
}

ご覧のとおり、各値がIntまたは になることを明示的に示していますString

応答

そしてもちろん、Responseタイプが必要です。

struct Response: Decodable {
    var values: [[IntOrString]]
}

デコード

これで、JSON を安全にデコードできます

if let response = try? JSONDecoder().decode(Response.self, from: data) {
    let values = response.values

    for value in values {
        for intOrString in value {
            switch intOrString {
            case .int(let int): print("It's an int: \(int)")
            case .string(let string): print("It's a string: \(string)")
            }
        }
    }
}

出力

It's an int: 1
It's an int: 1
It's an int: 7
It's a string: Azuan Child
It's a string: Anak Azuan
It's a string: 12345
It's a string: ACTIVE
It's a string: Morning
It's an int: 7
It's an int: 12
It's a string: 2017-11-09 19:45:00
It's an int: 28
It's an int: 1
It's an int: 0
It's a string: Azuan Child2
It's a string: Amran
It's a string: 123456
It's a string: ACTIVE
It's a string: Evening
It's an int: 1
It's an int: 29
It's a string: 2017-11-09 19:45:00
于 2018-04-27T17:31:54.470 に答える
2

JSON の内部配列にパターン化された型のシーケンスがあることに注目してください。そのシーケンスが何であるかがわかります。内部配列の型は、パターン化されたシーケンスになっています: 3 つの Int、5 つの String、2 つの Int、そしておそらく Date として意図されたものです。明らかに、JSON 設計者の頭の中では、これら 11 の要素のそれぞれに固定された既知の意味があります。

つまり、JSON 式全体を手動でダンプスター ダイビングしてデコードすることにより、11 の要素を 1 つずつ手動で取得できます。

配列には混合型があり、Swift はそれを好まないため、それらを Any (または AnyObject) の配列として表現する必要があります。しかし、それらを人工的な中間構造体にラップするのではなく、それ自体として取得できます。

ところで、各要素の意味がわかっている場合は、Any の配列の代わりに、各要素の意味を表す 11 個の名前付きプロパティを持つ構造体に内部配列をデコードできます。その方がきれいな結果になるのですが、11個の値の意味がわからないので使っていません。

どうぞ:

struct S : Decodable {
    var values : [[Any]]
    enum CodingKeys : String, CodingKey {
        case values
    }
    init(from decoder: Decoder) throws {
        // get the dictionary
        let con = try! decoder.container(keyedBy: CodingKeys.self)
        // get the "values" array of array
        var con2 = try! con.nestedUnkeyedContainer(forKey: CodingKeys.values)
        var bigarr = [[Any]]()
        for _ in 0..<con2.count! {
            // get a nested array
            var con3 = try! con2.nestedUnkeyedContainer()
            // decode all the elements of the nested array
            var arr = [Any]()
            arr.append(try! con3.decode(Int.self))
            arr.append(try! con3.decode(Int.self))
            arr.append(try! con3.decode(Int.self))
            arr.append(try! con3.decode(String.self))
            arr.append(try! con3.decode(String.self))
            arr.append(try! con3.decode(String.self))
            arr.append(try! con3.decode(String.self))
            arr.append(try! con3.decode(String.self))
            arr.append(try! con3.decode(Int.self))
            arr.append(try! con3.decode(Int.self))
            arr.append(try! con3.decode(String.self))
            bigarr.append(arr)
        }
        // all done! finish initialization
        self.values = bigarr
    }
}

let result = try! JSONDecoder().decode(S.self, from: jdata)
print(result.values)
// [[1, 1, 7, "Azuan Child", "Anak Azuan", "12345", "ACTIVE",
// "Morning", 7, 12, "2017-11-09 19:45:00"], 
// [28, 1, 0, "Azuan Child2", "Amran", "123456", "ACTIVE", 
// "Evening", 1, 29, "2017-11-09 19:45:00"]]
于 2017-11-10T04:23:30.200 に答える