0

All questions I have found so far on the searches I've done is about decoding nested JSON to some struct with nested properties. I want to do the opposite: decode flat JSON to a struct with nested properties.

Here's example JSON:

{
    "id":"ABC123",
    "cell":"test",
    "qty":24
}

which I'd like to decode to this struct:

struct InventoryItem {
    let id: String
    let mfgInfo: MfgInfo
}

extension InventoryItem {
    struct MfgInfo {
        let cell: String
        let qty: Int
    }
}

I have tried adding CodingKeys for each struct:

struct InventoryItem: Decodable {
    let id: String
    let mfgInfo: MfgInfo

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


struct MfgInfo: Decodable {
    let cell: String
    let qty: Int

    enum CodingKeys: String, CodingKey {
        case cell, qty
    }
}

But this doesn't work. I get this error:

No value associated with key CodingKeys(stringValue: \"mfgInfo\", intValue: nil) (\"mfgInfo\"), converted to mfg_info.

How can I make this work without a custom initializer? Or do I need to write a custom init(with: Decoder) initializer?

2
  • There is a full example for this in the documentation provided by Apple: developer.apple.com/documentation/foundation/… under "Encode and Decode Manually"! Commented Mar 9, 2020 at 20:01
  • @Mamaessen, I read that documentation, but it appears to deal with encoding nested JSON to a flat struct. I'm trying to do the opposite (flat JSON to nested struct). Commented Mar 9, 2020 at 20:03

2 Answers 2

1

I wasn't able to do this without implementing the init(from: Decoder) initializer, but it was easier than I thought. For nested structs that just have primitive types, you can just use the automatically synthesized init(from: Decoder) method.

Final struct setup:

struct InventoryItem: Decodable {
    let id: String
    let mfgInfo: MfgInfo

    enum CodingKeys: String, CodingKey {
        case id
    }

    struct MfgInfo: Decodable {
        let cell: String
        let qty: Int

        enum CodingKeys: String, CodingKey {
            case cell, qty
        }
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        id = try container.decode(String.self, forKey: .id)
        mfgInfo = try MfgInfo(from: decoder)
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

This is only possible with a custom init(with: Decoder) implementation, or by having e.g. a FlatInventoryItem type that conforms to Decodable and then providing conversion methods between that and your desired InventoryItem type.

1 Comment

This is kinda what I thought, but I was hoping there was a more concise way.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.