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

问题描述 投票:0回答:3

我有以下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库,我如何使用ParticipantparticipantHomeparticipantAway属性将参与者字段绑定到两个不同的side对象AWAYHOME来绑定字段。

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

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注释或自定义JAXB实现动态绑定这些元素?

java xml jackson jaxb xml-deserialization
3个回答
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'}}

1
投票

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


0
投票

这里有很多很棒的答案和替代品,但是我决定采用混合物与列表绑定并返回正确的homeaway团队,通过实施getter方法返回正确的主场或客队以基本上压扁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"));
}

谢谢您的帮助!

© www.soinside.com 2019 - 2024. All rights reserved.