基于属性值的Jackson XML绑定元素

时间:2019-04-04 21:27:38

标签: java xml jackson jaxb xml-deserialization

我具有以下XML结构:

<participants>
    <participant side="AWAY">
        <team id="18591" name="Orlando Apollos" />
    </participant>
    <participant side="HOME">
        <team id="18594" name="Memphis Express" />
    </participant>
</participants>

如果我将FasterXML Jackson库与JAXB批注一起使用,如何使用以下方法将参与者字段绑定到两个不同的Participant对象participantHomeparticipantAway sideAWAY的{​​{1}}属性来绑定字段。

使用以下对象显然不会起作用,因为存在重复的字段:

HOME

如何使用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; } 批注或自定义JAXB实现动态绑定那些元素?

3 个答案:

答案 0 :(得分:1)

您可以使用“参与者列表”代替两个不同的参与者。 用 @XmlAttribute(name =“ side”,required = true)注释 side 。 然后创建两个不同的参与者对象,并将它们添加到列表中。

答案 1 :(得分:1)

您需要编写自定义反序列化器,因为没有注释可以将列表项绑定到对象中的给定属性。如果您已经使用Jackson,请尝试实现自定义JsonDeserializer而不是自定义XmlAdapter。我们可以通过将内部Participant对象反序列化为Map来简化自定义反序列化程序。简单的例子:

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
}

打印:

Participants{participantHome=Participant{id=18594, name='Memphis Express'}, participantAway=Participant{id=18591, name='Orlando Apollos'}}

答案 2 :(得分:0)

这里有很多不错的答案和替代方案,但我决定采用结合列表的方法,并通过实现返回正确位置的getter方法来返回正确的homeaway团队或客队基本上将List弄平。在处理整个应用程序中的列表时,这将减少计算量。

我向父类(每个home / away参与者)添加了以下代码:

Participant getHome() {
    return (Participant) participants.stream()
            .filter(p -> p.getSide().equalsIgnoreCase("home"));
}

Participant getAway() {
    return (Participant) participants.stream()
            .filter(p -> p.getSide().equalsIgnoreCase("away"));
}

感谢您的帮助!