我需要以下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<>();
}
如何将此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>
我阅读了一些教程和示例,但我发现没有人所有的键都是xml的节点值。
修改
几个小时后,我接近解决方案。
我的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;
}
}
我的映射类:
@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;
}
}
我得到了以下结果:
<?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>
如何删除/跳过费率标记?
答案 0 :(得分:1)
我建议您构建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>
你有多个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;
....
}
答案 1 :(得分:1)
如果您想坚持问题开头所述的原始XML结构,那么使用@XmlJavaTypeAdapter
很难或无法解决。
但是你可以重用替代方法
answer to "JAXB nodes to map"并将其应用于您的情况:
在ExchRates
类中声明List<Element>
注释@XmlAnyElement
,以便JAXB将其用于编组/解组。
但您需要Map<CurrencyUnit, Map<CurrencyUnit, Double>>
或Map<String, Map<String, Double>>
。
(我不知道如何创建CurrencyUnit
,因此我的解决方案使用String
。)
因此,您也声明了这一点,但使用@XmlTransient
进行了注释,以便JAXB不会将其用于编组/解组。
最后实现一个私有方法afterUnmarshal(Unmarshaller unmarshaller, Object parent)
,您可以在其中将内容从List<Element>
铲到Map<String, Map<String, Double>>
。
如Unmarshal Event Callbacks中所述
JAXB将在适当的时候调用此方法。
如果您需要编写XML文件,您可能还需要一个私有方法beforeMmarshal(Marshaller marshaller)
,您可以将Map<String, Map<String, Double>>
中的内容反馈回List<Element>
。
如Marshal Event Callbacks中所述
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);
}
}
}