CoreOffice / XMLCoder

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

Encoding a CDATA wrapped object #76

Open timsearle opened 5 years ago

timsearle commented 5 years ago

I have a question around producing the following output:

<MsgData><![CDATA[
<foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="TOKEN">
<someElement>1111.2222.3333.4444</someElement>
<anotherElement>4000000000000065</anotherElement>
<test>31122019</test>
</foo>]]></MsgData>

How can I use XMLEncoder to produce? This element is nested within others but they've been omitted for clarity. All I can currently think of is encode the internal object separately, convert from Data into a String, and then encode with a CDATA encoding strategy.

I'm also slightly confused around how to conditionally use CData encoding on strings without enabling and disabling it on the encoder as I pass. Any thoughts or ideas would be hugely appreciated!

MaxDesiatov commented 5 years ago

Thank you for reporting this @timsearle and sorry for the delayed reply. I'm wondering if implementing some helpers for CDATA in the recently added DynamicNodeEncoding (#70) would be realistic. @JoeMatt would be interesting to hear your thoughts on this if possible 🙏

timsearle commented 5 years ago

Thanks Max! Just as an update, I'm currently working around this issue using something along the lines of:

struct CDATA<MessageData: Codable & XMLRootKey> {
    let data: MessageData

    init(contents: MessageData) {
        self.data = contents
    }

    func CDATARepresentation() -> String {
        let xmlEncoder = SOAP.encoder()

        guard let data = try? xmlEncoder.encode(data, withRootKey: data.rootKey),
            let result = String(data: data, encoding: .utf8) else {
                return ""
        }

        return "<![CDATA[\(result)]]>"
    }
}

extension CDATA: Codable {
    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        let data = try container.decode(String.self).data(using: .utf8) ?? Data()

        let decoder = SOAP.decoder()
        self.data = try decoder.decode(MessageData.self, from: data)
    }
}

Where SOAP.decoder() is a convenience for returning a special subclass of XMLCoder I've made with some custom encoding strategies.