Reputation: 798
I have the following XML
structure:
<participants>
<participant side="AWAY">
<team id="18591" name="Orlando Apollos" />
</participant>
<participant side="HOME">
<team id="18594" name="Memphis Express" />
</participant>
</participants>
If I am using the FasterXML Jackson
library with JAXB
annotations how can I bind the participants fields to two different Participant
objects participantHome
and participantAway
using the side
property of AWAY
and HOME
to bind the fields.
Using the following object won't work obviously because there are duplicate fields:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "participants")
public class Participants {
@XmlElement(name = "participant")
Participant participantHome;
@XmlElement(name = "participant")
Participant participantAway;
}
How can I dynamically bind those elements using JAXB
annotations or a custom JAXB
implementation?
Upvotes: 1
Views: 2993
Reputation: 798
Couple of great answers and alternatives here but I've decided to go with a hybrid of binding with a list and returning the correct home
or away
team by implementing getter methods that return the correct home or away team to essentially flatten the List
. This will reduce the amount of computation when processing the lists throughout the application.
I added the following code to my parent class (to each home
/away
participant):
Participant getHome() {
return (Participant) participants.stream()
.filter(p -> p.getSide().equalsIgnoreCase("home"));
}
Participant getAway() {
return (Participant) participants.stream()
.filter(p -> p.getSide().equalsIgnoreCase("away"));
}
Thanks for the help!
Upvotes: 0
Reputation: 38625
You need to write custom deserialiser because there is no annotation which allow to bind list item to given property in object. If you already use Jackson
try to implement custom JsonDeserializer
instead of custom XmlAdapter
. We can simplify our custom deserialiser by deserialising internal Participant
objects to Map
. Simple example:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class XmlMapperApp {
public static void main(String[] args) throws Exception {
File xmlFile = new File("./resource/test.xml").getAbsoluteFile();
XmlMapper xmlMapper = new XmlMapper();
Participants result = xmlMapper.readValue(xmlFile, Participants.class);
System.out.println(result);
}
}
class ParticipantsXmlAdapter extends JsonDeserializer<Participants> {
@Override
public Participants deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
List<Map<String, Object>> participants = readParticipantsMap(p, ctxt);
Participants result = new Participants();
for (Map<String, Object> participantMap : participants) {
Object side = participantMap.get("side").toString();
if ("AWAY".equals(side)) {
result.setParticipantAway(convert((Map<String, Object>) participantMap.get("team")));
} else if ("HOME".equals(side)) {
result.setParticipantHome(convert((Map<String, Object>) participantMap.get("team")));
}
}
return result;
}
private List<Map<String, Object>> readParticipantsMap(JsonParser p, DeserializationContext ctxt) throws IOException {
MapType mapType = ctxt.getTypeFactory().constructMapType(Map.class, String.class, Object.class);
JsonDeserializer<Object> mapDeserializer = ctxt.findRootValueDeserializer(mapType);
List<Map<String, Object>> participants = new ArrayList<>();
p.nextToken(); // skip Start of Participants object
while (p.currentToken() == JsonToken.FIELD_NAME) {
p.nextToken(); // skip start of Participant
Object participant = mapDeserializer.deserialize(p, ctxt);
participants.add((Map<String, Object>) participant);
p.nextToken(); // skip end of Participant
}
return participants;
}
private Participant convert(Map<String, Object> map) {
Participant participant = new Participant();
participant.setId(Integer.parseInt(map.get("id").toString()));
participant.setName(map.get("name").toString());
return participant;
}
}
@JsonDeserialize(using = ParticipantsXmlAdapter.class)
class Participants {
private Participant participantHome;
private Participant participantAway;
// getters, setters, toString
}
class Participant {
private int id;
private String name;
// getters, setters, toString
}
prints:
Participants{participantHome=Participant{id=18594, name='Memphis Express'}, participantAway=Participant{id=18591, name='Orlando Apollos'}}
Upvotes: 1
Reputation: 174
You can use List of Participant in place of two different participant. Annotate side with @XmlAttribute(name = "side", required = true). Then create two different Participant objects and add them to list.
Upvotes: 1