-1

I have a Json-file with multiple "Dozzs" objects. Inside each "Dozzs" object is one or multiple "Dozz" objects. So I have a mix of "Dozzs" objects with one "Dozz" and "Dozzs" objects with an array of "Dozz" objects. But I can't say in my struct let doz: [Dozz], JSONDecoder doesn't want to parse one single "Dozz" into a "Dozz"-array if that is the case.

Is there a way to parse(if there is only one Dozz object inside Dozzs) that one into an array? So I have always in my struct an Dozz-Array with one or more objects and JSONDecoder doesn't crashes.

Here is my current struct:

struct Dozzs : Codable {
 let doz : Dozz?
 //let doz: [Dozz]?
  }

Here is the json:

{
  "test" : [ 
    {
  "dozzs":  
                {
                    "doz": {
                        "-type": "Person",
                        "-key": "125"
                    }

      }
    },
            {
  "dozzs": [ 
                {
                    "doz": {
                        "-type": "Person",
                        "-key": "123"
                    }
                },
                {
                    "doz": {
                        "-type": "Person",
                        "-key": "124,"
                    }
                }
            ]

            }
  ]
}

Help would be great

  • 1
    Can you post more code regarding this. – Pravin Tate Oct 18 '19 at 15:58
  • Override the init and check if the value if an array or a solo (and if it's the case, create an array single object from it). – Larme Oct 18 '19 at 16:00
  • 1
    Add the JSON that you are trying to parse and the parsing code. – David S. Oct 18 '19 at 16:00
  • 1
    You should [edit] your question to include all relevant code in the form of a [mcve] along with some sample JSONs (in both forms, with a single `Dozz` object and with an array of them) to make the question on-topic. – Dávid Pásztor Oct 18 '19 at 16:00
  • 1
    Please add an example of your json so it easier for us understand what you're trying to do. Probably you'll need to override the `init(from decoder: Decoder)` function, using a if to detect if it's an array or just a single element. – Fantini Oct 18 '19 at 16:01
  • Possible duplicate of [JSON request doesn't always return same response type (object & array)](https://stackoverflow.com/questions/51468104/json-request-doesnt-always-return-same-response-type-object-array) – Larme Oct 18 '19 at 16:01

2 Answers2

1

I've put a sample together hope this can help you out.


let json1 = """
{
"doz": {
        "dozProp1": "prop1",
        "dozProp2": "prop2"
    }
}
"""
let json2 = """
{

"doz": [
{
"dozProp1": "prop1",
"dozProp2": "prop2"
},
{
"dozProp1": "prop1_23",
"dozProp2": "prop2_34"
}
]
}
"""

public struct Doz: Decodable {
    var dozProp1: String
    var dozProp2: String
}

public enum JSONValue: Decodable {
    case arrayDoz([Doz])
    case doz(Doz)

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let value = try? container.decode([Doz].self) {
            self = .arrayDoz(value)
        } else  if let value = try? container.decode(Doz.self) {
            self = .doz(value)
        } else {
            throw DecodingError.typeMismatch(JSONValue.self, DecodingError.Context(codingPath: container.codingPath, debugDescription: "Not doz"))
        }
    }
}


public struct DecodingDoz: Decodable {
    var doz: JSONValue
}

let parsed1: DecodingDoz = try JSONDecoder().decode(DecodingDoz.self, from: json1.data(using: .utf8)!)
let parsed2: DecodingDoz = try JSONDecoder().decode(DecodingDoz.self, from: json2.data(using: .utf8)!)

print(parsed1.doz)
print(parsed2.doz)

A brief explanation, so I've create an enum with the two possible values for Doz, and did an if let to check with type of Doz is being parsed from the json, and i case a match is found the parsing is applied other wise an exception is thrown.

Happy coding

Reginaldo Costa
  • 766
  • 7
  • 17
0

You should override the init from decoder and decode the JSON. In the following code, I'm trying to decode a single Doz object first, if it turns out to be nil, then trying to decode an array of Doz.

struct Dozzs : Decodable {
    let doz : [Dozz]?

    // Coding Keys
    enum MyDozzsKeys: String, CodingKey {
      case doz
    }

    // Overriding init
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: MyDozzsKeys.self)
        if let doz    = try? container.decode(Dozz.self, forKey: .doz) {
            self.doz  = [doz]
        } else {
            let doz   = try container.decode([Dozz].self, forKey: .doz)
            self.doz  = doz
        }
    }

 }

Update: Based on your JSON the model should be something like this:

struct Dozzs : Decodable {
    let dozzs : [Doz]?

    enum MyDozzsKeys: String, CodingKey {
      case dozzs
    }

    init(from decoder: Decoder) throws {
      let container = try decoder.container(keyedBy: MyDozzsKeys.self)
        if let doz = try? container.decode(Doz.self, forKey: .dozzs) {
            self.dozzs = [doz]
        } else {
            let doz = try container.decode([Doz].self, forKey: .dozzs)
            self.dozzs = doz
        }
    }

 }

struct Doz: Decodable {
    let doz: DozData
}

struct DozData: Decodable {
    let type: String
    let key: String
    enum CodingKeys: String, CodingKey {
        case type = "-type"
        case key = "-key"
    }
}

struct Test: Decodable {
    let test: [Dozzs]?
}

And decode like:

try JSONDecoder().decode(Test.self, from: jsonTest)
Midhun MP
  • 103,496
  • 31
  • 153
  • 200
  • This seems right, just one detail, you don't need to assign the value for the CodingKey key (`= "doz"`) if you don't write anything it'll take the rawValue with the name of the case, so just writing `case doz` would be enough – Fantini Oct 18 '19 at 16:10
  • @Fantini Yes, you are correct. No need to specify the value for that case. – Midhun MP Oct 18 '19 at 16:15
  • Hi, thanks for your answer, but I am still struggling. I am getting the following error: debugDescription: "Expected to decode Dictionary but found an array instead.", underlyingError: nil. I will add my json at the top. – Elektronenhirn Oct 18 '19 at 17:14
  • Now I added my json, can you please compare it? – Elektronenhirn Oct 18 '19 at 17:36
  • It is working. I did a small mistake. Thanks a lot for your awesome help :) – Elektronenhirn Oct 18 '19 at 19:58