Deserialize a child whether it's an Element or an Attribute
Is it currently possible to ignore child type and deserialize it just for the fact it's a child? Here's a minimal setup:
val xml = XML { }
@Serializable
data class Data(val child : String)
// Should be able to do both
xml.decodeFromString(Data.serializer(), "<Data child='child1'/>") // OK
xml.decodeFromString(Data.serializer(), "<Data><child>child1</child></Data>") // NOPE
For reference, I'm trying to interface with a server I don't control and it sometimes returns the first format, sometimes the second, depending on the running version (with no indication of the running version in the response!)
You should be able to use either @XmlValue otherChildren: List<Node> or @XmlValue otherChildren: List<CompactFragment>
But the format doesn't support this fallback automatically. You could try a custom recovery in the policy.
Before asking this question, I checked if one of the policy attributes could've helped me achieve my goal, and I couldn't figure it out. I suppose what you're suggesting is that I implement a XmlSerializationPolicy instead? Or even override the default one? Or do you provide a shortcut that I'm not aware of, like unknownChildHandler?
Thanks for your guidance
I am suggesting unknown child handler (this doesn't require updating the policy), as you can return the resolved property. I haven't tested it though in terms of whether it is able to ignore the input kind (tag vs attribute). An alternative would be to transform the input.
Transforming the input to "normalize" it, converting child elements to attrs (and in the process dropping their own attrs) was what I went for.
I think I had trouble doing it via the unknownChildHandler because I didn't know if what I was doing was correct. Here's what I tried for posterity:
UnknownChildHandler { input, kind, _, qname, candidates ->
if (kind == InputKind.Element)
candidates
.filterIsInstance<PolyInfo>()
.filter { it.tagName == qname }
.map { XML.ParsedData(it.index, input.allText() }
else
emptyList()
}
I'm not too familiar with the API (first time using it) and I couldn't find docs/examples for how the handler is used. Maybe there's something wrong with doing it this way. I did some guessing, some source reading, some testing, and it seemed to work. I was hesitant to go for it nonetheless. This snippet is public domain. feel free to include in the examples if you deem it correct and worth including.
@hyphenrf (and readers) the most important consideration for recovery is to leave the input in a valid state. In this case I would go for .single/.singleOrNull instead of .filter for a bit more robustness (although you should only have a single element in the list anyway - the alternative would break decoding in many ways).