[英]XmlAdapter and XmlIDREF in moxy jaxb
我正在嘗試使用MOXy JAXB來序列化類A,它看起來像:
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public class A {
private Map<Foo, Bar> fooBar = new HashMap<Foo, Bar>();
private Set<Foo> foos = new HashSet<Foo>();
@XmlJavaTypeAdapter(FooBarMapAdapter.class)
public Map<Foo, Bar> getFooBar() {
return fooBar;
}
public void setFooBar(Map<Foo, Bar> fooBar) {
this.fooBar = fooBar;
}
@XmlElement
public Set<Foo> getFoos() {
return foos;
}
public void setFoos(Set<Foo> foos) {
this.foos = foos;
}
}
關鍵是“foos”字段中的Foo對象是fooBar映射中的Foo對象的超集。 因此,我想將“fooBar”映射的關鍵元素“鏈接”到“foos”列表中的相應元素。 我使用XmlID和XmlIDREF注釋嘗試了這個:
@XmlAccessorType(XmlAccessType.NONE)
public class Foo {
private String xmlId;
@XmlID
@XmlAttribute
public String getXmlId() {
return xmlId;
}
public void setXmlId(String xmlId) {
this.xmlId = xmlId;
}
}
@XmlAccessorType(XmlAccessType.NONE)
public class Bar {
// Some code...
}
然后在我的XmlAdapter中,我嘗試在改編后的映射條目的foo對象上使用XmlIDREF注釋:
public class FooBarMapAdapter extends
XmlAdapter<FooBarMapAdapter.FooBarMapType, Map<Foo, Bar>> {
public static class FooBarMapType {
public List<FooBarMapEntry> entries = new ArrayList<FooBarMapEntry>();
}
@XmlAccessorType(XmlAccessType.NONE)
public static class FooBarMapEntry {
private Foo foo;
private Bar bar;
@XmlIDREF
@XmlAttribute
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
@XmlElement
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}
@Override
public FooBarMapType marshal(Map<Foo, Bar> map) throws Exception {
FooBarMapType fbmt = new FooBarMapType();
for (Map.Entry<Foo, Bar> e : map.entrySet()) {
FooBarMapEntry entry = new FooBarMapEntry();
entry.setFoo(e.getKey());
entry.setBar(e.getValue());
fbmt.entries.add(entry);
}
return fbmt;
}
@Override
public Map<Foo, Bar> unmarshal(FooBarMapType fbmt) throws Exception {
Map<Foo, Bar> map = new HashMap<Foo, Bar>();
for (FooBarMapEntry entry : fbmt.entries) {
map.put(entry.getFoo(), entry.getBar());
}
return map;
}
}
編組上面的代碼時按預期工作並生成以下XML:
<?xml version="1.0" encoding="UTF-8"?>
<a>
<fooBar>
<entries foo="nr1">
<bar/>
</entries>
</fooBar>
<foos xmlId="nr1"/>
</a>
為了測試unmarshal,我使用以下測試代碼:
public class Test {
public static void main(String[] args) throws Exception {
A a = new A();
Map<Foo, Bar> map = new HashMap<Foo, Bar>();
Foo foo = new Foo();
foo.setXmlId("nr1");
Bar bar = new Bar();
map.put(foo, bar);
a.setFooBar(map);
a.setFoos(map.keySet());
final File file = new File("test.xml");
if (!file.exists())
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
JAXBContext jc = JAXBContext.newInstance(A.class);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(a, fos);
FileInputStream fis = new FileInputStream(file);
Unmarshaller um = jc.createUnmarshaller();
A newA = (A) um.unmarshal(fis);
System.out.println(newA.getFooBar());
}
}
此代碼生成(對我來說)意外結果:
{null=test.moxy.Bar@373c0b53}
也就是說,在地圖中用作鍵的Foo對象為null。 如果我更改地圖適配器並封送Foo對象兩次,而不是使用ID引用,我不會得到這個空指針。
我已經能夠在谷歌上使用JAXB-RI找到一些關於這個的帖子,在這里可以解決問題寫一個IDResolver,如http://weblogs.java.net/blog/2005/08/15/pluggable-所述 - ididref-handling-jaxb-20 。 不幸的是,我無法在MOXy JAXB JavaDoc中找到有關此類的任何信息。
對解決方法的建議從Blaise Doughan的回答中,我意識到這是JAXB的MOXy實現中的一個錯誤。 我已經能夠為這個bug制作一個(丑陋的)解決方法。 我們的想法是,不是使用XMLAdapter,而是在其定義類中“轉換”地圖。 A類現在看起來像:
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public class A {
private Map<Foo, Bar> fooBar = new HashMap<Foo, Bar>();
private Set<Foo> foos = new HashSet<Foo>();
// Due to a bug a XMLAdapter approch is not possible when using XmlIDREF.
// The map is mapped by the wrapper method getXmlableFooBarMap.
// @XmlJavaTypeAdapter(FooBarMapAdapter.class)
public Map<Foo, Bar> getFooBar() {
return fooBar;
}
public void setFooBar(Map<Foo, Bar> fooBar) {
this.fooBar = fooBar;
}
@XmlElement
public Set<Foo> getFoos() {
return foos;
}
public void setFoos(Set<Foo> foos) {
this.foos = foos;
}
// // WORKAROUND FOR JAXB BUG /////
private List<FooBarMapEntry> mapEntries;
@XmlElement(name = "entry")
public List<FooBarMapEntry> getXmlableFooBarMap() {
this.mapEntries = new LinkedList<FooBarMapEntry>();
if (getFooBar() == null)
return mapEntries;
for (Map.Entry<Foo, Bar> e : getFooBar().entrySet()) {
FooBarMapEntry entry = new FooBarMapEntry();
entry.setFoo(e.getKey());
entry.setBar(e.getValue());
mapEntries.add(entry);
}
return mapEntries;
}
public void setXmlableFooBarMap(List<FooBarMapEntry> entries) {
this.mapEntries = entries;
}
public void transferFromListToMap() {
fooBar = new HashMap<Foo, Bar>();
for (FooBarMapEntry entry : mapEntries) {
fooBar.put(entry.getFoo(), entry.getBar());
}
}
}
在unmarshal之后,現在需要調用transferFromListToMap方法。 因此,在獲得對newA的引用后,應立即添加以下行:
newA.transferFromListToMap();
任何有關更好的解決方法/錯誤修復的建議將不勝感激:)。
注意:我是EclipseLink JAXB(MOXy)的負責人。
我已經能夠確認您所看到的問題:
為什么問題正在發生
問題是由於MOXy在處理XmlAdapter
邏輯之前處理@XmlIDREF
邏輯。 MOXy執行XML文檔的單次傳遞,並在@XmlIDREF
處理@XmlIDREF
關系以確保已構建所有引用的對象(因為引用可能在引用的對象之前,如本例所示)。
我將嘗試針對此問題發布變通方法,您可以使用上述錯誤跟蹤我們在此問題上的進展。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.