CoreOffice / XMLCoder

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

How do I handle encoding and decoding an element and attribute with the same name but different types? #193

Open EricSites opened 4 years ago

EricSites commented 4 years ago

How do I handle encoding and decoding an element and attribute with the same name but different types?

<!ELEMENT barline (segno?, coda?)>
<!ATTLIST barline
    segno CDATA #IMPLIED
    coda CDATA #IMPLIED
>
<!ELEMENT segno EMPTY>
<!ATTLIST segno
    %optional-unique-id;
    %smufl;
>
<!ELEMENT coda EMPTY>
<!ATTLIST coda
    %optional-unique-id;
    %smufl;
>
<!ENTITY % smufl
    "smufl %smufl-glyph-name; #IMPLIED">
<!ENTITY % optional-unique-id
    "id ID #IMPLIED">
<!ENTITY % smufl-glyph-name "NMTOKEN">

Example XML

<barline coda="name-1">
    <coda id="1" smufl="coda-char" />
</barline>

Here is what I have done so far, but doesn't work:

struct Segno: Codable, Equatable, DynamicNodeDecoding, DynamicNodeEncoding {
    var id: String? // %optional-unique-id
    var smufl: String? // %smufl

    enum CodingKeys: String, CodingKey {
            case id
            case smufl
    }
    static func nodeDecoding(for key: CodingKey) -> XMLDecoder.NodeDecoding {
            switch key { default: return .attribute }
    }
    static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
            switch key { default: return .attribute }
    }
}

struct Coda: Codable, Equatable, DynamicNodeDecoding, DynamicNodeEncoding {
    var id: String? // %optional-unique-id
    var smufl: String? // %smufl

    enum CodingKeys: String, CodingKey {
            case id
            case smufl
    }
    static func nodeDecoding(for key: CodingKey) -> XMLDecoder.NodeDecoding {
            switch key { default: return .attribute }
    }
    static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
            switch key { default: return .attribute }
    }
}

struct Barline: Codable, Equatable, DynamicNodeDecoding, DynamicNodeEncoding {
    var segnoE: Segno?
    var codaE: Coda?
    var segno: String? // CDATA #IMPLIED
    var coda: String? // CDATA #IMPLIED

    enum CodingKeys: String, CodingKey {
        case segno
        case coda
    }
    static func nodeDecoding(for key: CodingKey) -> XMLDecoder.NodeDecoding {
            switch key {
                case CodingKeys.coda: return .attribute
                case CodingKeys.segno: return .attribute
                default: return .element
            }
    }
    static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
            switch key {
                case CodingKeys.coda: return .attribute
                case CodingKeys.segno: return .attribute
                default: return .element
            }
    }
}
owenzhao commented 4 years ago

For Codable, one property is with one key. In you sample, coda in attribute and element shares the same key. Two properties are with one key. That is a conflict. I don't think there is a way for this other than solving the conflict first.

EricSites commented 4 years ago

For Codable, one property is with one key. In you sample, coda in attribute and element shares the same key. Two properties are with one key. That is a conflict. I don't think there is a way for this other than solving the conflict first.

This is a valid XML schema that I can't change. I need to parse it.

owenzhao commented 4 years ago

For Codable, one property is with one key. In you sample, coda in attribute and element shares the same key. Two properties are with one key. That is a conflict. I don't think there is a way for this other than solving the conflict first.

This is a valid XML schema that I can't change. I need to parse it.

You don't need to change the xml file. Just add another layer.

  1. read the xml as String.
  2. replacing all "<coda" to something like "<codaE"
  3. convert the String to Data.
  4. use XMLCoder decode the data.