简体   繁体   English

用Jaxb解组嵌套地图

[英]Unmarshal nested Map with Jaxb

I need the following DTO 我需要以下DTO

@XmlRootElement(name = "exchangerate")
@XmlAccessorType(XmlAccessType.FIELD)
public class ExchRates {

    @XmlJavaTypeAdapter(DateAdapter.class)
    private Date date;

    @XmlJavaTypeAdapter(JaxbExchangeRatesMapAdapter.class)
    private Map<CurrencyUnit, Map<CurrencyUnit, Double>> rates = new HashMap<>();
}

How can I unmarshal this xml into the DTO above? 我如何将该XML解组到上面的DTO中?

<exchangerate>
    <date>2015-05-04</date>
    <EUR>
        <EUR>1</EUR>
        <GBP>0.73788</GBP>
        <USD>1.1152</USD>
    </EUR>
    <GBP>
        <EUR>1.35523</EUR>
        <GBP>1</GBP>
        <USD>1.51136</USD>
    </GBP>
    <USD>
        <EUR>0.8967</EUR>
        <GBP>0.66166</GBP>
        <USD>1</USD>
    </USD>
</exchangerate>

I read some tutorials and examples but I found no one where all keys are the node values of the xml. 我读了一些教程和示例,但没有发现所有键都是xml的节点值的人。

Edit 编辑

After some hours I'm close to a solution. 几个小时后,我就要解决了。

My XmlAdapter: 我的XmlAdapter:

public class JaxbExchangeRatesMapAdapter extends XmlAdapter<JaxbExchangeRatesMap, Map<CurrencyUnit, Map<CurrencyUnit, Double>>> {

    @Override
    public Map<CurrencyUnit, Map<CurrencyUnit, Double>> unmarshal(JaxbExchangeRatesMap v) throws Exception {
        return null;
    }

    @Override
    public JaxbExchangeRatesMap marshal(Map<CurrencyUnit, Map<CurrencyUnit, Double>> v) throws Exception {
        JaxbExchangeRatesMap map = new JaxbExchangeRatesMap();

        for (CurrencyUnit currencyFrom : v.keySet()) {
            Map<CurrencyUnit, Double> from = v.get(currencyFrom);
            JaxbExchangeRatesEntry entry = new JaxbExchangeRatesEntry();
            for (CurrencyUnit currencyTo : from.keySet()) {
                entry.getEntries().add(new JAXBElement<>(new QName(currencyTo.getCurrencyCode()), Double.class, from.get(currencyTo)));
            }
            JAXBElement<JaxbExchangeRatesEntry> jaxbElement = new JAXBElement<>(new QName(currencyFrom.getCurrencyCode()), JaxbExchangeRatesEntry.class, entry);
            map.getEntires().add(jaxbElement);
        }
        return map;
    }

}

And my mapped classes: 和我的映射类:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso(JaxbExchangeRatesEntry.class)
public class JaxbExchangeRatesMap extends Printable {

    private static final long serialVersionUID = 15543456767150881L;

    @XmlAnyElement
    private List<JAXBElement<JaxbExchangeRatesEntry>> entires = new ArrayList<>();

    public List<JAXBElement<JaxbExchangeRatesEntry>> getEntires() {
        return entires;
    }

    public JaxbExchangeRatesMap setEntires(List<JAXBElement<JaxbExchangeRatesEntry>> entires) {
        this.entires = entires;
        return this;
    }
}

@XmlAccessorType(XmlAccessType.FIELD)
public class JaxbExchangeRatesEntry extends Printable {

    private static final long serialVersionUID = -694282168028218725L;

    @XmlAnyElement
    private List<JAXBElement<Double>> entries = new ArrayList<>();

    public List<JAXBElement<Double>> getEntries() {
        return entries;
    }

    public JaxbExchangeRatesEntry setEntries(List<JAXBElement<Double>> entries) {
        this.entries = entries;
        return this;
    }
}

With that I got the following result: 这样我得到了以下结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<exchangerate>
    <rates>
        <USD>
            <USD>9.0</USD>
            <EUR>7.0</EUR>
            <GBP>8.0</GBP>
        </USD>
        <EUR>
            <USD>3.0</USD>
            <EUR>1.0</EUR>
            <GBP>2.0</GBP>
        </EUR>
        <GBP>
            <USD>6.0</USD>
            <EUR>4.0</EUR>
            <GBP>5.0</GBP>
        </GBP>
    </rates>
</exchangerate>

How can I remove/skip the rates tag? 如何删除/跳过费率标签?

I would recommend that you structure your XML like: 我建议您将XML结构如下:

<exchangerate>
    <date>2015-05-04</date>
    <currency code="EUR">
        <rate code="EUR">1</rate >
        <rate code="GBP">0.73788</rate >
        <rate code="USD">1.1152</rate >
    </currency>
    <currency code="GBP">
        <rate code="EUR">1.35523</rate >
        <rate code="GBP">1</rate >
        <rate code="USD">1.51136</rate >
    </currency>
    <currency code="USD">
        <rate code="EUR">0.8967</rate >
        <rate code="GBP">0.66166</rate >
        <rate code="USD">1</rate >
    </currency>
</exchangerate> 

and you have multiple Classes: 并且您有多个课程:

@XmlAccessorType(XmlAccessType.FIELD)
public class ExchangeRates {
    @XmlJavaTypeAdapter(DateAdapter.class)
    private Date date;

    @XmlElement(name="currency")
    private List<Currency> currencies = new ArrayList<>();

    ....
}

@XmlAccessorType(XmlAccessType.FIELD)
public class Currency {
    @XmlAttribute
    private String code;

    @XmlElement(name="rate")
    private List<Rate> rates= new ArrayList<>();

    ....
}


@XmlAccessorType(XmlAccessType.FIELD)
public class Rate {
    @XmlAttribute
    private String code;

    @XmlValue
    private Double value;

    ....
}

If you want to stick to your original XML structure as described at the beginning of your question, then it is difficult or impossible to solve with @XmlJavaTypeAdapter . 如果您要坚持问题开头所描述的原始XML结构,那么使用@XmlJavaTypeAdapter很难或不可能解决。 But you can reuse the alternative approach from the answer to "JAXB nodes to map" and apply it to your situation: 但是您可以重复使用从答案到“要映射的JAXB节点”的替代方法,并将其应用于您的情况:

In your ExchRates class declare a List<Element> annotated with @XmlAnyElement , so that JAXB will use it for marshalling/unmarshalling. 在您的ExchRates类中,声明一个用@XmlAnyElement注释的List<Element> ,以便JAXB将其用于编组/解组。

But you want a Map<CurrencyUnit, Map<CurrencyUnit, Double>> or Map<String, Map<String, Double>> . 但您需要Map<CurrencyUnit, Map<CurrencyUnit, Double>>Map<String, Map<String, Double>> (I don't know how to create CurrencyUnit s, therefore my solution uses String .) Hence you declare that too, but annotated with @XmlTransient , so that JAXB will not use it for marshalling/unmarshalling. (我不知道如何创建CurrencyUnit ,因此我的解决方案使用String 。)因此,您也声明了这一点,但使用@XmlTransient注释,因此JAXB不会将其用于编组/解组。

Finally implement a private method afterUnmarshal(Unmarshaller unmarshaller, Object parent) where you shovel contents from the List<Element> to the Map<String, Map<String, Double>> . 最后,在afterUnmarshal(Unmarshaller unmarshaller, Object parent)实现一个私有方法,在该方法中,您将内容从List<Element>铲到Map<String, Map<String, Double>> As described in Unmarshal Event Callbacks JAXB will call this method at appropriate times. Unmarshal事件回调中所述,JAXB将在适当的时间调用此方法。

If you need to write XML files, you may also need a private method beforeMmarshal(Marshaller marshaller) where you shovel contents from the Map<String, Map<String, Double>> back to the List<Element> . 如果需要编写XML文件,则可能还需要在beforeMmarshal(Marshaller marshaller)使用私有方法,在该方法中,将内容从Map<String, Map<String, Double>>返回到List<Element> As described in Marshal Event Callbacks JAXB will call this method at appropriate times. 元帅事件回调中所述,JAXB将在适当的时间调用此方法。

@XmlRootElement(name = "exchangerate")
@XmlAccessorType(XmlAccessType.FIELD)
public class ExchRates {

    private Date date;

    @XmlAnyElement
    private List<Element> elements;

    @XmlTransient  // don't participate in JAXB marshalling/unmarshalling
    private Map<String, Map<String, Double>> rates;

    @SuppressWarnings("unused")  // called only by JAXB
    private void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        rates = new HashMap<>();
        for (Element element : elements) {
            String currencyUnit = element.getTagName();
            NodeList subElements = element.getElementsByTagName("*");
            Map<String, Double> subMap = new HashMap<>();
            for (int i = 0; i < subElements.getLength(); i++) {
                Element subElement = (Element) subElements.item(i);
                String currencyUnit2 = subElement.getTagName();
                double value = Double.parseDouble(subElement.getTextContent());
                subMap.put(currencyUnit2, value);
            }
            rates.put(currencyUnit, subMap);
        }   
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM