CoreOffice / XMLCoder

Easy XML parsing using Codable protocols in Swift
https://coreoffice.github.io/XMLCoder/
MIT License
795 stars 107 forks source link

Decoding Dictionary #220

Open MartinP7r opened 3 years ago

MartinP7r commented 3 years ago

This might be more general Codable related problem. Please let me know if you think it's beyond the scope of this project.

I have some xml data that looks like this:

<some_type>
 // ....
  <q_codes>
    <q_code type="a">123</q_code>
    <q_code type="b">456</q_code>
    <q_code type="c">789</q_code>
    <q_code type="d">0</q_code>
  </q_codes>
</some_type>

Which seems to me would be most fitting to decode into a dictionary, like

["a": 123, "b": 456, "c": 789, "d": 0]

Inside the parent struct:

struct SomeType: Codable, ... {
    // ...
    let qCodes: [String: Int]
}

However, I haven't found a clean way to do this with normal container decoding and XMLCoder. What I am currently doing is using a stand-in struct with NodeEncoding that's basically private to SomeType and just used to decompose the xml structure and somewhat awkwardly transform it into a dictionary inside init(from decoder: Decoder):

struct QCode: Codable, DynamicNodeEncoding {
    let value: Int
    let type: String

    enum CodingKeys: String, CodingKey {
        case value = "", type = "rad_type"
    }
    static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
        switch key {
        case CodingKeys.type: return .attribute
        default: return .element
        }
    }
}

// ...

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

    let qCodeData = try container.decodeIfPresent(QCode.self, forKey: .qCodes)
    qCodes = qCodeData?.qCode.reduce(into: [String: Int](), { $0[$1.type] = $1.value }) ?? [:]
    // ...
}

I was wondering if there's a better approach to decoding dictionary-like xml structures like these.