XMLCoder icon indicating copy to clipboard operation
XMLCoder copied to clipboard

Encoding a CDATA wrapped object

Open timsearle opened this issue 5 years ago • 2 comments

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!

timsearle avatar Feb 18 '19 18:02 timsearle

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 🙏

MaxDesiatov avatar Feb 25 '19 13:02 MaxDesiatov

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.

timsearle avatar Feb 25 '19 13:02 timsearle