limbo
limbo

Reputation: 369

Decoding nested JSON when the keys at the lowest level are the same

I need your help on decoding this piece of JSON

{
    "json": {
        "firstItem": {
            "key": "key",
            "value": "data"
        },
        "secondItem": {
            "key": "key",
            "value": "data"
        },
        "thirdItem": {
            "key": "key",
            "value": "data"
        },.......... //All of the data, which is a lot, comes like this
    }
}

I want to be able to decode this data, but can't figure out a way without having to write too much code because there are a lot of items. Is it possible to be able to decode this JSON maybe into a nesting of dictionaries, or even better into an array of item objects with just key and value attributes? Thanks in advance

Upvotes: 2

Views: 178

Answers (4)

Leo Dabus
Leo Dabus

Reputation: 236448

If you don't know the keys of the nesting json you can simply decode it as a dictionary of items:

struct Root: Decodable {
    let json: [String: Item]
}

struct Item: Decodable {
    let key, value: String
}

let json = """
{
    "json": {
        "firstItem": {
            "key": "key",
            "value": "data"
        },
        "secondItem": {
            "key": "key",
            "value": "data"
        },
        "thirdItem": {
            "key": "key",
            "value": "data"
        }
    }
}
"""

do {
    let items = try JSONDecoder().decode(Root.self, from: .init(json.utf8)).json.map(\.value)
    print(items)
} catch {
    print(error)
}

This will print:

[Item(key: "key", value: "data"), Item(key: "key", value: "data"), Item(key: "key", value: "data")]

Upvotes: 0

Witek Bobrowski
Witek Bobrowski

Reputation: 4259

With Swifts structs combined with Codable we can achieve very clean and nice solution to the problem.

Declare Item struct

helper struct will be useful when retrieving values from each item contained within the json dictionary

struct Item: Codable {
    let key: String
    let value: String
}

Decode the JSON

now, considering the json is a string value, you can decode it easily with JSONDecoder

let jsonString: String // your json here

guard let data = jsonString.data(using: .utf8) else { return }
let decoder = JSONDecoder()
do {
    let decoded = try decoder.decode([String: [String: Item]].self, from: data)
    let itemsDict: [String: Item] = decoded["json"] ?? [:]

    // do what you want with the items!
    let firstItem: Item? = itemsDict["firstItem"]
    let items: [Item] = itemsDict.values
} catch {
    print(error)
}

Upvotes: 1

Joakim Danielson
Joakim Danielson

Reputation: 52013

Create a custom type for your key/value pairs

struct KeyValue: Decodable {
    let key: String
    let value: String
}

and then decode them into an array like this

do {
    let result = try JSONDecoder().decode([String: [String: KeyValue]].self, from: data)
    let array = result["json"]?.reduce(into: []) { $0.append($1.value) }
} catch {
    print(error)
}

Upvotes: 0

Daniel T.
Daniel T.

Reputation: 33967

I went old school with my answer:

guard let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: [String: [String: Any]]] else { return }
let keyValues = json["json"].map { $0.values.map { ($0["key"]! as! String, $0["value"]!) } }

Upvotes: 0

Related Questions