CoreOffice / XMLCoder

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

DynamicNodeEncoding Works with List Type, but Not with List's Element Type #216

Open owenzhao opened 3 years ago

owenzhao commented 3 years ago
// Model

class Foo:Object, Codable, DynamicNodeEncoding {
    enum CodingKeys: String, CodingKey {
        case id
        case numbers = "number"
    }

    static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
        switch key {
        case CodingKeys.id:
            return .attribute
        default:
            return .element
        }
    }

    @objc dynamic var id = ObjectId.generate()
    let numbers = List<Bar>()

    override class func primaryKey() -> String? {
        return "id"
    }
}

class Bar:Object, Codable, DynamicNodeEncoding {
    enum CodingKeys: String, CodingKey {
        case id
    }

    static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
        switch key {
        case CodingKeys.id:
            return .attribute
        default:
            return .element
        }
    }

    @objc dynamic var id = ObjectId.generate()

    override class func primaryKey() -> String? {
        return "id"
    }
}
// code

let foo = Foo()
foo.numbers.append(objectsIn:[Bar(),Bar(),Bar()])

let encoder = XMLEncoder()
encoder.outputFormatting = .prettyPrinted
let encodedXML = try! encoder.encode(foo, withRootKey: "foo")

let str = String(data: encodedXML, encoding: .utf8)
print(str!)

Results:

<foo id="606bedefcd66ce1115c94be3">
    <number>
        <id>606bedefcd66ce1115c94be6</id>
    </number>
    <number>
        <id>606bedefcd66ce1115c94be7</id>
    </number>
    <number>
        <id>606bedefcd66ce1115c94be8</id>
    </number>
</foo>

Expected: The ids in number should be attributes instead of elements.

If I change let numbers = List<Bar>() to var numbers = [Bar](), the issue is gone. So I think there must be something wrong here and I don't know what to do next. As the Realm requests to use List instead of Array here. Any suggestions?

owenzhao commented 3 years ago

List

截屏2021-04-06 下午1 28 19
owenzhao commented 3 years ago

Solved. I read the source code of XMLCoder, and in DynamicNodeEncoding.swift, there is

extension Array: DynamicNodeEncoding where Element: DynamicNodeEncoding {
    public static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
        return Element.nodeEncoding(for: key)
    }
}

So I make it as a copy for List

extension List: DynamicNodeEncoding where Element: DynamicNodeEncoding {
    public static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
        return Element.nodeEncoding(for: key)
    }
}
// results

<foo id="606bf34ba0066ab968b69b2b">
    <number id="606bf34ba0066ab968b69b2e" />
    <number id="606bf34ba0066ab968b69b2f" />
    <number id="606bf34ba0066ab968b69b30" />
</foo>

And it worked. So I guess for people who use Realm, you should do this too.