Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read nested array and tuples in JSON in Swift

I have some data passed via JSON which is an array of tuples containing arrays, and I can't extract the data out of it. I've tried to cast to different types, but the only thing that works is [Any] and I can't break that down further. The JSON:

{
  ...
  // stuff that I can easily read, like "WIDTH": 33,
  ... 
  "WIDTHS_ROI_PAIRS": [[80, [0,0,200,160]], [145, [0, 240, 100, 60]], [145, [100, 240, 100, 60]]]
}

The struct it is meant to go into:

struct WidthRoiPair: Codable {
    let width: Int
    let roi: [Int]
}

What I want to do (it doesn't work, take it as pseudo-code):

let widthRoiPairsTmp = json["WIDTHS_ROI_PAIRS"] as! [Any]
for p in widthRoiPairsTmp {
    let pair = WidthRoiPair(width: p.0 as! Int, roi:p.1 as! [Int])
    widthRoiPairs.append(pair)
}

Trying p[0] instead of p.0 also doesn't work, trying to cast the JSON directly to what I need, something like this:

let widthRoiPairsTmp = json["WIDTHS_ROI_PAIRS"] as! [(Int, [Int])]

also doesn't work. I tried to use JSONDecoder() but I don't know how to pass json["WIDTHS_ROI_PAIRS"] (or its elements) to it (how to convert it back to Data). I'm sure the answer is obvious to anyone with a little more experience with Swift, but at the moment I'm totally stuck...

like image 462
thebucc Avatar asked Sep 03 '25 10:09

thebucc


1 Answers

You can try

struct Root: Codable {
    let widthsRoiPairs: [[InnerItem]]

    enum CodingKeys: String, CodingKey {
        case widthsRoiPairs = "WIDTHS_ROI_PAIRS"
    }
}

enum InnerItem: Codable {
    case integer(Int)
    case integerArray([Int])

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Int.self) {
            self = .integer(x)
            return
        }
        if let x = try? container.decode([Int].self) {
            self = .integerArray(x)
            return
        }
        throw DecodingError.typeMismatch(InnerItem.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for InnerItem"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .integer(let x):
            try container.encode(x)
        case .integerArray(let x):
            try container.encode(x)
        }
    }
}

Usage

let str = """

            {
            "WIDTHS_ROI_PAIRS": [[80, [0,0,200,160]], [145, [0, 240, 100, 60]], [145, [100, 240, 100, 60]]]
            }

"""

    do {

        let res = try JSONDecoder().decode(Root.self, from: str.data(using: .utf8)!)

        res.widthsRoiPairs.forEach {

            $0.forEach {

                switch $0 {
                case .integer(let w) :
                    print(w)
                case .integerArray(let arr) :
                    print(arr)
                }

            }
        }

    }
    catch {

        print(error)
    }
like image 50
Sh_Khan Avatar answered Sep 05 '25 01:09

Sh_Khan