[英]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.