CoreOffice / XMLCoder

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

How to not include a CodingKey in encoded data? #277

Closed Treata11 closed 1 year ago

Treata11 commented 1 year ago

How to not include a CodingKey in encoded data without using empty String?

I've faced an issue trying to encode some data where I don't want the coding keys to exist when the data is encoded! The example is a bit long & complex, but it should clarify the situation.

Signature

Not much to this struct; Just note that the CodingKeys of this one has to be presented in the xml file: (<beats>1</beats> & <beat-type>8</beat-type>) have to be included & are the only exceptions:

```Swift struct Signature: Equatable, Codable { let beats: String let beatType: String
    init(beats: String, beatType: String) {
        self.beats = beats
        self.beatType = beatType
    }

    enum CodingKeys: String, CodingKey {
        case beats
        case beatType = "beat-type"
    }
}

### Measured
Here's another struct which uses the first one for one of its vars. The presence of `<signature>` & `<interchangeable>` is not required in the encoded representation of this struct:
```Swift
    struct Measured: Equatable, Codable {
        var signature: Signature
        var interchangeable: Interchangeable?

        init(signature: Signature, interchangeable: Interchangeable? = nil) {
            self.signature = signature
            self.interchangeable = interchangeable
        }

    enum CodingKeys: CodingKey {
        case signature
        case interchangeable
    }

    init(from decoder: Decoder) throws {
        let signatureContainer = try decoder.container(keyedBy: Time.Signature.CodingKeys.self)
        self.signature = Time.Signature(
            beats: try signatureContainer.decode(String.self, forKey: .beats),
            beatType: try signatureContainer.decode(String.self, forKey: .beatType)
        )
        let container = try decoder.container(keyedBy: Time.Measured.CodingKeys.self)
        self.interchangeable = try container.decodeIfPresent(Interchangeable.self, forKey: .interchangeable)
        }
    }

Kind

Again, I don't want the measured & unmeasured coding keys to be presented in the encoded data:

enum Kind: Equatable, Codable {
        case measured(Measured)
        case unmeasured(Unmeasured)

    enum CodingKeys: CodingKey {
        case measured
        case unmeasured
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case let .measured(value):
            try container.encode(value, forKey: .measured)
        case let .unmeasured(value):
            try container.encode(value, forKey: .unmeasured)
        }
    }
}

Results

How do I omit the <measured> & <signature> keys from the xml?

let expected = Time("1", "8", symbol: .singleNumber) // "1" & "8" are initializing Signature vars
let encoded = try! encoder.encode(decoded, withRootKey: "time")

the xml output:

<time symbol="single-number">
            <measured>
            <signature>
                <beats>1</beats>
                <beat-type>8</beat-type>
            </signature>
        </measured>
</time>