-1

I have the following json data and trying to parse it, but I get number of objects, but object itself in the array all are nil.

I do not want to decode the origin in the following json object.

By the way, the following string is first converted to data and then passed it to parse function below.

Data is follows:

[ 
 [
   {"id": "152478", "age": 20},
   {"character": "king","isDead":"no", "canMove" :"yes", "origin" :"south africa"}
 ],
 [
  {"id": "887541", "age": 22},
  {"character": "lion", "isDead":"no", "canMove" :"yes", "origin" :"south america"}
 ]
]

Models

struct A: Codable {
    let id: String?
    let age: Int?

    enum CodingKeys: String, CodingKey {
        case id
        case age
    }
}

struct B: Codable {
    let character, isDead, canMove: String?

    enum CodingKeys: String, CodingKey {
        case character
        case isDead
        case canMove
    }
}

struct AB :Codable {
  let a: A
  let b: B

  init(from decoder: Decoder) throws {
    guard var container = try? decoder.unkeyedContainer() else { 
         //no error here!!!       
         fatalError()   
     }
    print(container)
    guard let a = try? container.decode(A.self),
        let b = try? container.decode(B.self)
      else {
        // throw since we didn't find A first, followed by B
        throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: nil)
        )
      }
    self.a = a
    self.b = b
  }
}

ViewModel

private func parse(jsonData : Data){
    do {
        let decoder = JSONDecoder()
        let response = try decoder.decode([AB].self, from: jsonData)
        print(response)
    }
    catch (let error as NSError) {
        print(error)
    }
}

UPDATE: By the way, the following code works. I wonder why above code does not handle?

private func parseData(jsonData : Data)
{
    do {
        response = try JSONSerialization.jsonObject(with: jsonData) as! [[[String: Any]]]
        for i in 0..<response.count
        {
            for j in 0..<response[i].count
            {
                if j == 0
                {
                    let jsonDat = (try? JSONSerialization.data(withJSONObject:response[i][j]))!
                    let b = try JSONDecoder().decode(A.self, from: jsonDat)
                }
                else if j == 1
                {
                    let jsonDatt = (try? JSONSerialization.data(withJSONObject:response[i][j]))!
                    let a = try JSONDecoder().decode(B.self, from: jsonDatt)
                }
            }
        }
        print(response)
    }
    catch let error as NSError {
        print(error)
    }
}

UPDATE II:

If I make the following changes [AB] --> [[AB]], and then I call it as follows, it decodes data, but in the array I end up, A object has values, but B nil, or vice versa.

let response = try decoder.decode([[AB]].self, from: jsonData)


guard var container = try? decoder.singleValueContainer() else
{
    fatalError()
}
casillas
  • 16,351
  • 19
  • 115
  • 215
  • What do you mean by "origin"? – New Dev Jun 12 '20 at 01:23
  • there is a property called `origin` in the B data. – casillas Jun 12 '20 at 01:24
  • 1
    Ah. Sorry, didn't notice. And which objects are nil? Do you mean that they have `nil` values? – New Dev Jun 12 '20 at 01:28
  • yes in the ViewModel `response` data has number of array objects, but values are all `nil`. By the way `response` has right number of objects in the array. – casillas Jun 12 '20 at 01:29
  • It errors out during decoding because you changed `age` to be `Int`, but in your JSON it's a string. – New Dev Jun 12 '20 at 01:36
  • In the Struct, it is `Int` and also in the json it is `Int` as well. – casillas Jun 12 '20 at 01:38
  • 1
    Ah.. I was foiled again by your previous question :) – New Dev Jun 12 '20 at 01:39
  • 1
    I ran your code and it works for me. – New Dev Jun 12 '20 at 01:45
  • this isn't a valid JSON `["id": "152478", "age": 20]` – New Dev Jun 12 '20 at 02:23
  • Sorry, I put it back my json object. My original json format is the right one. However, I am still getting the same issue. – casillas Jun 12 '20 at 02:38
  • I have also added the code which I am using and it works, but I do not like it. – casillas Jun 12 '20 at 02:43
  • I have added another update regarding the `singleValueContainer` decoder. – casillas Jun 12 '20 at 03:23
  • Print the `error`, the **real** error. It's **not** an `NSError` instance. Delete `let error as NSError` – vadian Jun 12 '20 at 03:48
  • `guard var container = try? decoder.unkeyedContainer() else { fatalError() }`, here I am getting fatal error. – casillas Jun 12 '20 at 03:58
  • Remove the ? in `try?` and catch the error. Remove also the guard expression and decode simply `a = try container.decode(A.self)`. Let the framework throw the error if there is one. By the way the code works with the given JSON. – vadian Jun 12 '20 at 04:10
  • @casillas - The code you have in the question (that you carried over from your previous question) works just fine for your JSON (you don't even need the CodingKey enums). So, if there is an error, it's not captured here – New Dev Jun 12 '20 at 04:45
  • @vadian, it does not give any error, but objects are nil – casillas Jun 12 '20 at 05:27
  • I have only called` let a = try container.decode(A.self)`, then it worked. then I tried same for `let b = try container.decode(B.self)`, then all B objects are nil. Even though data is there. – casillas Jun 12 '20 at 06:27
  • 1
    Then the JSON is not the JSON in the question. As I said the code does work with this JSON – vadian Jun 12 '20 at 07:12

1 Answers1

1

I just tried this stripped-down version of your code in a playground

let json = """
[
 [
   {"id": "152478", "age": 20},
   {"character": "king","isDead":"no", "canMove" :"yes", "origin" :"south africa"}
 ],
 [
  {"id": "887541", "age": 22},
  {"character": "lion", "isDead":"no", "canMove" :"yes", "origin" :"south america"}
 ]
]
""".data(using: .utf8)!

struct A: Codable {
    let id: String
    let age: Int
}

struct B: Codable {
    let character, isDead, canMove: String
}

struct AB: Codable {
    let a: A
    let b: B

    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()

        self.a = try container.decode(A.self)
        self.b = try container.decode(B.self)
    }
}

do {
    let ab = try JSONDecoder().decode([AB].self, from: json)
    print(ab.count)
    print(ab[0].a.id)
    print(ab[0].b.character)

    print(ab[1].a.id)
    print(ab[1].b.character)
} catch {
    print(error)
}

and it works just fine. Maybe this helps figuring out what's going on.

Gereon
  • 17,258
  • 4
  • 42
  • 73
  • I have only called `let a = try container.decode(A.self)`, then it worked. then I tried same for `let b = try container.decode(B.self)`, then all B objects are nil. Even though data is there. – casillas Jun 12 '20 at 06:24
  • If you remove either the decoding of `A.self` or `B.self` in this example, you'll get a compiler error, so I don't really understand that "then it worked" means in this context. I suggest you try stripping your example down to a **reproducible** example like mine and update your question with that, otherwise it's really hard to help you. – Gereon Jun 12 '20 at 08:33
  • I have been using `String?` and `Int?` to avoid crashes and getting `nil` instead. – casillas Jun 12 '20 at 19:22
  • Making the properties optional in my version does not change the behaviour in any meaningful way. Like others have said already, I strongly suspect the JSON you're dealing with is not the one you've posted. Again, unless you present a reproducible example with real data, it's almost impossible to help you. – Gereon Jun 12 '20 at 19:57