0

Lets have a json

{
    "channelId": 100,
    "channel_name": "STV 1",
        "stream": {
            "URL": "www.rtvs.sk",
            "DRM": "secureMedia",
            "drmKeys": ["1", "2", "3"],
            "userInfo": {
                "user": "Michal23",
                "userIsTester": true
            }
        }
}

and a struct:

struct Channel : Codable {
    var channelId : Int
    var channelName : String
    var channelUrl : URL

    private enum CodingKeys : String, CodingKey {
        case channelId
        case channelName = "channel_name"
        case channelUrl = "URL" <===??? json path somehow?
    }
}

I would like to fetch URL from nested stream, but without creating nested struct for it. Is it possible? How?

4
  • you need to use nested structs as this is how the JSONDecoder and codable work together. There are external libraries like ObjectMapper which allow you to add a json path like "streams.URL" but JSONDecoder doesn't. You can however decode it with Decodable manually as described here and get nested info: developer.apple.com/documentation/foundation/… Commented Feb 13, 2018 at 12:46
  • I was afraid that it is the only way. Thanks for your comment. Commented Feb 13, 2018 at 12:48
  • see updated comment above, there is a way, but its more manual Commented Feb 13, 2018 at 12:48
  • Thanks for kicking me in right direction. Helped a lot. Commented Feb 13, 2018 at 13:08

1 Answer 1

4

Looking at the documentation, you can do it but it is more of a manual process than usual. You need to decode the nested container and then extract the information using the coding key.

//: Playground - noun: a place where people can play

import UIKit

let jsonData = """
{
    "channelId": 100,
    "channel_name": "STV 1",
    "stream": {
        "URL": "www.rtvs.sk"
    }
}
""".data(using: String.Encoding.utf8)!

struct Channel {
    var channelId : Int
    var channelName : String
    var channelUrl: URL

    private enum CodingKeys : String, CodingKey {
        case channelId
        case channelName = "channel_name"
        case stream
    }

    private enum AdditionalInfoKeys: String, CodingKey {
        case channelUrl = "URL"
    }
}

extension Channel: Decodable {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        channelId = try values.decode(Int.self, forKey: .channelId)
        channelName = try values.decode(String.self, forKey: .channelName)

        let additionalInfo = try values.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .stream)
        channelUrl = try additionalInfo.decode(URL.self, forKey: .channelUrl)

    }
}

let decoder = JSONDecoder()
let channel = try? decoder.decode(Channel.self, from: jsonData)
print(channel)

OUTPUT: Channel(channelId: 100, channelName: "STV 1", channelUrl: www.rtvs.sk))

Sign up to request clarification or add additional context in comments.

2 Comments

Yes, this is it. I made almost the same solution, except for initializer not being in extension, but in the struct itself. Is there some value to have it in extension? (if it is not your code, yes, but when it is all your code you can edit at will)
P.S. you don't need to escape " after """.

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.