I found a lot of articles that describe how to unmarshal a sequence of XML elements to a HashMap as long as they are within a "parent" element. However, I do not get this to work with the children directly under the root element!
Option 1 - Works:
<?xml version="1.0" encoding="UTF-8"?>
<checks>
<checks>
<check key="check1"/>
<check key="check2"/>
...
</checks>
</checks>
Option 2 - Does not work:
<?xml version="1.0" encoding="UTF-8"?>
<checks>
<check key="check1"/>
<check key="check2"/>
...
</checks>
Checks:
package com.foo.conf;
import java.util.Map;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement(name="checks")
public class Checks {
@XmlJavaTypeAdapter(ChecksAdapter.class)
@XmlElement(name="checks")
public Map<String, Check> checkMap;
}
Check:
package com.foo.conf;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlValue;
public class Check {
@XmlAttribute public String key;
@XmlValue public String description;
public Check() { }
public Check(String key) {
this.key = key;
}
public String getCheckKey() {
return this.key;
}
}
CheckMapType:
package com.foo.conf;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
class CheckMapType {
@XmlElement(name="check")
public List<Check> checkList; // = new ArrayList<Check>();
}
ChecksAdapter:
package com.foo.conf;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.adapters.XmlAdapter;
final class ChecksAdapter extends XmlAdapter<CheckMapType, Map<String, Check>> {
@Override
public CheckMapType marshal(Map<String, Check> arg0) throws Exception {
return null;
}
@Override
public Map<String, Check> unmarshal(CheckMapType arg0) throws Exception {
System.out.println("u: " + arg0.checkList.size());
Map<String, Check> map = new HashMap<String, Check>();
for (Check check : arg0.checkList) {
System.out.println(check);
map.put(check.key, check);
}
return map;
}
}
This is (some dummy test lineS) how I generate the classes/invoke the unmarshalling:
JAXBContext jc = JAXBContext.newInstance(Checks.class);
Unmarshaller u = jc.createUnmarshaller();
Checks c = (Checks) u.unmarshal(new File("checks.xml"));
System.out.println(c.checkMap.size());
Any idea on how to get option #2 to work? It works when using a List instead of the Map but I need the HashMap as I have to access the objects by the given keys...
Any hints much appreciated!
How are you generaing the JAXB classes? I am not sure what exactly are you trying to do but the below very simple code works for me ..
and here is my JAXB generated classes ..
ChecksType
(Root element)And
checkType
(the child)Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
JAXB will treat each object relationship with a nesting relationship.
Map
is treated like anObject
instead of aCollection
so this is why you are getting the behaviour that you are seeing.MOXy has an XPath based mapping extension called
@XmlPath
that could be used for this use case.For More Information