I'm using Retrofit and Jackson with XML parser to transform this XML into data classes. As you can see it's a very rare array inside child_group... there is one item and one info, one item and one info, etc... not a normal array.
<?xml version="1.0" encoding="utf-8"?>
<parent id="2261">
<child_group>
<item>
<line>1</line>
</item>
<info></info>
<item>
<line>2</line>
</item>
<info></info>
<item>
<line>3</line>
</item>
<info></info>
</child_group>
</parent>
I can't find any info about how to create the data class structure to parse this. What can I try next?
This data class structure passed to retrofit only parses the first item and the first info, but not the rest of them:
@JacksonXmlRootElement(localName = "parent")
data class Parent(
val id: String,
@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "child_group")
val child_group: ChildGroup
)
data class ChildGroup(
@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "item")
val item: List<Item>,
val info: String? = null
)
data class Item(
val line: String
)
This is my Retrofit builder:
Retrofit.Builder().baseUrl("https://www.website.es/")
.addConverterFactory(
JacksonConverterFactory.create(XmlMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.registerKotlinModule()))
.build().create(ApiService::class.java)
I tried also doing this, with same result:
@JacksonXmlRootElement(localName = "parent")
data class Parent(
val id: String,
@JacksonXmlProperty(localName = "child_group")
@JacksonXmlElementWrapper(useWrapping = false)
val childGroup: List<ChildGroupItem>
)
data class ChildGroupItem(
val item: Item,
val info: String? = null
)
data class Item(
val line: String
)
This problem is similar to https://stackoverflow.com/a/79388160/860227. If you manage to sort all items in child_group
(all item
tags come first, followed by info
), your solution will work.
You need to create a custom deserializer for ChildGroup class like:
class CustomDeserializer : JsonDeserializer<ChildGroup>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): ChildGroup {
val items = mutableListOf<Item>()
val infos = mutableListOf<String>()
val node = parser.codec.readTree<JsonNode>(parser)
node.fields().forEach { (key, value) ->
when (key) {
"item" -> {
if (value.isArray) {
value.forEach { itemNode ->
val item = parser.codec.treeToValue(itemNode, Item::class.java)
items.add(item)
}
} else {
val item = parser.codec.treeToValue(value, Item::class.java)
items.add(item)
}
}
"info" -> {
if (value.isArray) {
value.forEach { itemNode ->
infos.add(itemNode.asText())
}
} else {
infos.add(value.asText())
}
}
}
}
return ChildGroup(items, infos)
}
}
and register it in the data class:
@JacksonXmlRootElement(localName = "parent")
data class Parent(
val id: String,
@JsonDeserialize(using = CustomDeserializer::class) // <-- this
val child_group: ChildGroup
)
data class ChildGroup(
@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "item")
val item: List<Item>,
val info: List<String>,
)
data class Item(
val line: String
)