I'm using Jackson (jackson-dataformat-xml, v2.13.0) to map an XML string into a corresponding Kotlin object. The XML looks like this:
<tide>
<service cominfo="Some text..."/>
<locationdata>
<location name="Oslo" code="OSL" latitude="59.904794"
longitude="10.753642" delay="0" factor="1.00" obsname="Oslo"
obscode="OSL" descr="Tidevann fra Oslo"/>
<reflevelcode>CD</reflevelcode>
<data type="prediction" unit="cm">
<waterlevel value="88.5" time="2021-10-22T06:50:00 01:00" flag="high"/>
<waterlevel value="69.8" time="2021-10-22T11:53:00 01:00" flag="low"/>
<waterlevel value="95.1" time="2021-10-22T19:05:00 01:00" flag="high"/>
</data>
</locationdata>
</tide>
I'm having a hard time extracting whatever is inside the data field, because it contains both attribute values (type and unit) as well as a list of waterlevel.
In my controller, I'm doing a simple mapping:
val mapper: ObjectMapper = XmlMapper.builder()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)
.build()
val tide: Tide = mapper.readValue(tideLevels, Tide::class.java)
I am unable to get everything from the data tag. It seems to me as if I have to choose between attributes and sub elements. The following mapping:
data class Tide(
val service: TideServiceInfo? = null,
val locationdata: LocationData? = null
)
data class LocationData(
val location: Location? = null,
val reflevelcode: String = "",
val data: TideData? = null
)
data class TideData(
val type: String = "",
val unit: String = "",
val waterlevel: List<WaterLevel> = emptyList()
)
data class WaterLevel(
val value: String = "",
val time: String = "",
val flag: String = ""
)
Gives me resulting json with an empty waterlevel-array:
{
"service": {
"cominfo": "Some text..."
},
"locationdata": {
"location": {shortened for brevity},
"reflevelcode": "CD",
"data": {
"type": "prediction",
"unit": "cm",
"waterlevel": []
}
}
}
But if I instead choose to map to the following data classes:
data class Tide(
val service: TideServiceInfo? = null,
val locationdata: LocationData? = null
)
data class LocationData(
val location: Location? = null,
val reflevelcode: String = "",
val data: List<WaterLevel> = emptyList()
)
data class WaterLevel(
val value: String = "",
val time: String = "",
val flag: String = ""
)
My result json has the data field set, without type and unit:
{
"service": {
"cominfo": "Some text..."
},
"locationdata": {
"location": { shortened for brevity },
"reflevelcode": "CD",
"data": [
{
"value": "88.5",
"time": "2021-10-22T06:50:00 01:00",
"flag": "high"
},
{
"value": "69.8",
"time": "2021-10-22T11:53:00 01:00",
"flag": "low"
},
{
"value": "95.1",
"time": "2021-10-22T19:05:00 01:00",
"flag": "high"
}
]
}
}
I have tried looking into annotations to put on data fields, e.g. @JacksonXmlProperty, but I don't see how that can help me achieve my solution. Am I missing something obvious here?
CodePudding user response:
Add
@field:JacksonXmlProperty(isAttribute = true)
to your attribute fields. See the docs for more info.
Note that you need @field
in Kotlin to apply the annotation to the actual object field
CodePudding user response:
The suggestions from @Evgeny did not work for me, but it did lead me on the right path searching on.
Basically, the answer suggested here, introducing the useWrapping=false
is what turned out to do the trick.
So I got what I wanted using the following:
@field:JacksonXmlElementWrapper(useWrapping = false)
val waterlevel: List<WaterLevel> = emptyList()