Je suis en train d'utiliser Moxy JAXB pour sérialiser une classe A qui ressemble à:XMLAdapter et XmlIDREF dans Moxy JAXB
@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;
}
}
Le point est que les objets Foo dans les champs « foos » sont plus nombreuses que les dans la carte fooBar. Par conséquent, je voudrais "lier" les éléments clés pour la carte "fooBar" aux éléments correspondants dans la liste "foos". Je l'ai essayé en utilisant les annotations XmlID et 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...
}
Puis dans mon XMLAdapter j'ai essayé d'utiliser une annotation XmlIDREF sur les entrées de carte adaptées de l'objet foo:
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;
}
}
Lorsque marshaling le code ci-dessus fonctionne comme prévu et produit le code XML suivant:
<?xml version="1.0" encoding="UTF-8"?>
<a>
<fooBar>
<entries foo="nr1">
<bar/>
</entries>
</fooBar>
<foos xmlId="nr1"/>
</a>
Pour les tests unmarshal, je suis en utilisant le test code suivant:
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());
}
}
Ce code produit le (pour moi) résultat inattendu:
{[email protected]}
C'est l'objet Foo utilisé comme clé dans la carte est nulle. Si je change l'adaptateur de carte et marsha l'objet Foo deux fois, au lieu d'utiliser une référence d'ID, je n'obtiens pas ce pointeur null.
J'ai été en mesure de trouver quelques messages à ce sujet sur google en utilisant le JAXB-RI, où le problème pourrait être résolu en écrivant un IDResolver comme décrit au http://weblogs.java.net/blog/2005/08/15/pluggable-ididref-handling-jaxb-20. Malheureusement, je n'ai pas été en mesure de trouver des informations sur une telle classe dans le MOXy JAXB JavaDoc.
Suggestion pour solution De réponse Blaise Doughan, je l'ai réalisé que ce bogue dans la mise en œuvre de Moxy JAXB. J'ai été capable de faire une solution de contournement (moche) pour ce bug. L'idée est que, au lieu d'utiliser un XMLAdapter, la carte est "convertie" dans sa classe de définition. La classe A ressemble maintenant:
@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());
}
}
}
Après la unmarshal, la méthode transferFromListToMap-doit maintenant être appelé. Ainsi, la ligne suivante doit être ajoutée immédiatement après la référence à nouveauA est obtenu:
newA.transferFromListToMap();
Toutes les suggestions pour une plus belle solution/correction d'un bug sera apprécié :).
Je rencontre également un problème, peut-être lié, en marshaling une méthode \ @XmlIDREF, à un objet ayant une annotation \ @XmlJavaTypeAdapter sur sa classe. Ici, l'objet entier est marshalé au lieu de spécifier uniquement l'ID de l'objet. –