How to decode nested JSON object using struct?

Hello, I am relatively new to Swift, and I recently encountered an JSON data as following

{
game_id: 123456
move: [1, 2, 12345678, null, { blur: 12345, sgf_downloaded_by: [ ] } ]
move_number: 12
}

I originally decoded this JSON data with

extension rtGameMove {
    init(dictionary: [String: Any]) throws {
        self = try JSONDecoder().decode(rtGameMove.self, from: JSONSerialization.data(withJSONObject: dictionary))
    }
}
 
struct rtGameMove: Decodable {
    var game_id: Int?
    var move: [QuantumVal]
    var move_number: Int?
}
 
enum QuantumVal: Decodable {
    case int(Int)
    private enum Codes: String, CodingKey {
        case int
    }
    public init (from decoder: Decoder) throws {
        if let int = try? decoder.singleValueContainer().decode(Int.self) {
            let move: Int = try decoder.singleValueContainer().decode(Int.self)
            self = .int(int)
            return
        }
        //Only the numerical values are useful for me, and the {blur: 12345, sgf_downloaded_by: [] }] is not important
        self = .int(0)
    }
}

However, when I attempted to retrieve the numerical values as Int in move, the Xcode said that I could not convert QuantumVal to Int. I wonder how could I retrieve the numerical values in move? Thank you so much!

Not the nicest but probably the shortest option:

let v = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
let game_id = v["game_id"] as! Int
let move_number = v["move_number"] as! Int
let movesArray = v["move"] as! [Any]
let moves = movesArray.map { $0 as? Int ?? 0 }

or do you need to necessarily implement it in a specific way via a struct + Decodable?

Thank you soooo much! It is very helpful!!

You're on the right track by creating an enum to represent the possible types in the array. If you then want to extract one of the associated values, it's most common to offer those as computed properties on the enum type itself. For example:

var int: Int? {
  guard case let .int(value) = self else { return nil }

  return value
}

Instead of returning int(0), you could make the associated value Int? and then return nil when decoding fails, then compactMap out the result.

More generally, you can create a specific type to help decode arrays where you don't want to fail the decoding if you can't decode a value. Typically this is done with some sort of FailableDecodable wrapper, where you'd decode a [FailableDecodable<Int>] and then extract just the Ints at the end.

Terms of Service

Privacy Policy

Cookie Policy