2017-11-10 20 views
1
{ 
"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"] 
] 
} 

Ok, das ist mein json-Format, das ich vom Server empfangenSwift 4 JSON Dekodierbare mit multidimensionalen und Mehrfachtyp Array

Im Moment habe ich es in meine Struktur entschlüsseln wollen, aber noch kein Glück drauf.

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

Und mein Anrufer Methode auf URLSession dieser

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() 

aussehen und die Fehler, die ich habe sind

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)) 

Ich weiß, in dem Array ein int gibt es und ich nur String werfen für die Werte var values: [[String]]? (der Grund, warum dieser Fehler Popup), aber ich kann einfach nicht mehrdimensionale Array oder Tupel in meinen Strukturen, da es dem Protokoll von Decodable folgen.

Ich kann auch nicht die Daten in Wörterbuch konvertieren, da es Fehler werfen „Expected Wörterbuch entschlüsseln, aber gefunden Array statt“

Irgendwelche Ideen auf, dieses Problem zu lösen? Ich habe versucht, String-Typ auf Daten, aber immer noch kein Glück ...

p/s: Wenn alle Json-Format sind in String-Typ, es gäbe kein Problem, aber ich habe nicht die Erlaubnis, das zu ändern, da ich anrufe es von API.

Antwort

1

Wie Sie schon gesagt haben, Ihr JSON-Array ist Multi-Typ, aber Sie versuchen, alle in String zu decodieren. Die Standardkonformität von String bis Decodable lässt dies nicht zu. Die einzige Lösung, die mir in den Sinn kommt, ist die Einführung eines neuen Typs.

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

+0

Dank, sehr nett, aber das ist die beste Praxis auszuführen Verwendung für einzelne Zeichenfolge, Single-Array und mehrdimensionales Array sein, wenn mit mehreren Umgang Geben Sie ein Array von JSON ein, das für JSONDecoder verwendet werden soll. –

+0

Es ist das erste Mal, dass ich mit Multi-Typ-Array konfrontiert. Um ehrlich zu sein, ich dachte, dass Json ungültig ist. Persönlich, anstatt ein Array von Multi-Typ-Array zu senden, würde ich ein Array von Objekten aus dem Backend senden. –

+0

Es könnte besser sein, eine Enum als eine Struktur zu verwenden - wenn das mit einem Decoder möglich ist. – JeremyP

1

Beachten Sie, dass die innere Arrays in der JSON eine strukturierte Folge von Datentypen hat, und wir wissen, was diese Sequenz ist. Die Typen in den inneren Arrays sind in einer gemusterten Sequenz: 3 Intents, 5 Strings, 2 Intents und etwas, das wahrscheinlich als Date gedacht ist. In den Köpfen der JSON Designer hat jedes dieser 11 Elemente eine feste und bekannte Bedeutung.

Dies bedeutet, dass wir die 11 Elemente nacheinander manuell per Dumpster-Tauchen aufnehmen und den gesamten JSON-Ausdruck manuell dekodieren können.

Die Arrays haben gemischte Typen, und Swift mag das nicht, also müssen wir sie als Array Any (oder AnyObject) ausdrücken; aber wir können erhalten sie als selbst, anstatt sie in eine künstliche Zwischenstruktur einzupacken.

Übrigens, wenn Sie wissen, was die Bedeutung jedes Elements ist, dann können Sie anstelle eines Arrays von Any das innere Array in eine Struktur mit 11 benannten Eigenschaften dekodieren, die ausdrücken, was jedes Element bedeutet. Das wäre ein saubereres Ergebnis, aber ich habe es nicht verwendet, weil ich die Bedeutung der 11 Werte nicht kenne. Hier

we go:

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"]] 
+0

schön, so kann es nur verwendet werden, wenn wir den Typ und wie viele Elemente im Array kennen, oder? –

+0

Richtig! Aber wir wissen das. Dieser JSON gibt jedem dieser Array-Elemente eindeutig einen _type_ und einen _meaning_. Wenn Sie wissen, was es ist, können Sie die inneren Arrays zu einem _struct_ analysieren, das Sie später weiterbringt. – matt

+0

Ja, danke, dass du dich danach erkundigt hast; Wie ich schon sagte, ich wollte das ausprobieren. – matt

0

Ich habe versucht, Ihre Lösung auf meinem Projekt verwenden, und seine Arbeit wie ein Zauber.Im Folgenden werde ich ein wenig geändert haben, so kann es

struct TripModel: Decodable { 
var tx_result: Any 
var columns: [Any] 
var values: [[Any]] 

enum CodingKeys : String, CodingKey { 
    case tx_result 
    case columns 
    case values 
} 

init(from decoder: Decoder) throws { 
    var bigarr = [[Any]]() 
    var arrColumn = [Any]() 
    // get the dictionary 
    let con = try! decoder.container(keyedBy: CodingKeys.self) 

    let conResult = try! con.decode(String.self, forKey: CodingKeys.tx_result) 

    var conColumns = try! con.nestedUnkeyedContainer(forKey: CodingKeys.columns) 
    //print(String(describing: conColumns.count)) 

    // get the "values" array of array 
    var con2 = try! con.nestedUnkeyedContainer(forKey: CodingKeys.values) 

    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(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(Int.self)) 
     arr.append(try! con3.decode(Int.self)) 
     arr.append(try! con3.decode(Double.self)) 
     arr.append(try! con3.decode(String.self)) 
     bigarr.append(arr) 
    } 

     arrColumn.append(try! conColumns.decode(String.self)) 
     arrColumn.append(try! conColumns.decode(String.self)) 
     arrColumn.append(try! conColumns.decode(String.self)) 
     arrColumn.append(try! conColumns.decode(String.self)) 
     arrColumn.append(try! conColumns.decode(String.self)) 
     arrColumn.append(try! conColumns.decode(String.self)) 
     arrColumn.append(try! conColumns.decode(String.self)) 
     arrColumn.append(try! conColumns.decode(String.self)) 
     arrColumn.append(try! conColumns.decode(String.self)) 
     arrColumn.append(try! conColumns.decode(String.self)) 
     arrColumn.append(try! conColumns.decode(String.self)) 

    // all done! finish initialization 
    self.tx_result = conResult 
    self.columns = arrColumn 
    self.values = bigarr 
} 

}