8

I want to convert a HashMap in a POJO class to XML. I tried using the XmlAdapter but it results in only the key and value pairs of the HashMap being the attributes of the XML Elements. I need the Key to be the Element itself and the value of the HashMap to be the value of the element. For instance, I need the following XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <cart> <supervisor_id>555</supervisor_id> <payments> <payment sequence="1"> <amount>123.45</amount> <billing_method>12345</billing_method> <form>card</form> <delivery_mode>Q</delivery_mode> </payment> <payment sequence="2"> <amount>123.45</amount> <person_id>2333</person_id> <form>cash</form> <delivery_mode>Q</delivery_mode> </payment> </payments> </cart> 

I created the following classes: MyMapType holds a list of MyMapEntryType class which has two fields namely Key and Value. How do I change the Key element to be @XmlElement and assign the value field to the Key field?


Here are my source files.

MyMapType.java

import java.util.ArrayList; import java.util.List; public class MyMapType { private List<MyMapEntryType> entry = new ArrayList<MyMapEntryType>(); public List<MyMapEntryType> getEntry() { return entry; } public void setEntry(List<MyMapEntryType> entry) { this.entry = entry; } } 

MyMapEntryType.java

import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlValue; @XmlAccessorType(XmlAccessType.FIELD) public class MyMapEntryType { @XmlAttribute private String key; @XmlValue private String value; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } 

Please also find the adapter class:

MyMapAdapter.java

import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.xml.bind.annotation.adapters.XmlAdapter; public class MyMapAdapter extends XmlAdapter<MyMapType, Map<String, String>> { @Override public MyMapType marshal(Map<String, String> map) throws Exception { MyMapType myMapType = new MyMapType(); for(Entry<String, String> entry : map.entrySet()) { MyMapEntryType myMapEntryType = new MyMapEntryType(); myMapEntryType.setKey(entry.getKey()); myMapEntryType.setValue(entry.getValue()); myMapType.getEntry().add(myMapEntryType); } return myMapType; } @Override public Map<String, String> unmarshal(MyMapType map) throws Exception { HashMap<String, String> hashMap = new HashMap<String, String>(); for(MyMapEntryType myEntryType : map.getEntry()) { hashMap.put(myEntryType.getKey(), myEntryType.getValue()); } return hashMap; } } 

This is the class which has the HashMap field:

XmlElementMap.java

@XmlAccessorType(XmlAccessType.FIELD) public class XmlElementMap { @XmlAttribute(name="sequence") private int sequence; @XmlJavaTypeAdapter(MyMapAdapter.class) private Map<String, String> map = new HashMap<String, String>(); public int getSequence() { return sequence; } public void setSequence(int sequence) { this.sequence = sequence; } public Map<String, String> getMap() { return map; } public void setMap(Map<String, String> map) { this.map = map; } } 


Please advise on how to achieve this.

Regards,
-Anand

Currently it produces the following output:

1
  • I am also a bit stuck in a similar issue during the unmarshalling. I am trying to unmarshal unknown elements into Map<String,Object> but it's not working as expected. I have posted the complete question in the below-provided link. If you get a chance can you please have a look at it and provide your suggestion: stackoverflow.com/questions/67648941/… Commented May 23, 2021 at 18:33

2 Answers 2

15

I have the same requirement "I need the Key to be the Element itself and the value of the HashMap to be the value of the element".

I didn't use customized adapter, but implemented it by converting the HashMap entries dynamically to a list of JAXBElement objects, and then annotated the list with @XmlAnyElement.

@XmlRootElement(name="root") public class MyMapType { @XmlAnyElement public List<JAXBElement> entries = new ArrayList<JAXBElement>(); public MyMapType() { // JAXB required } public MyMapType(Map<String, String> map) { for (Map.Entry<String, String> entry : map.entrySet()) { entries.add(new JAXBElement(new QName(entry.getKey()), String.class, entry.getValue())); } } public static void main(String[] args) throws Exception { JAXBContext context = JAXBContext.newInstance(MyMapType.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); Map<String, String> map = new LinkedHashMap<String, String>(); map.put("key1", "value1"); map.put("key2", "value2"); MyMapType mt = new MyMapType(map); marshaller.marshal(mt, System.out); } } 

The output is,

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <root> <key1>value1</key1> <key2>value2</key2> </root> 
Sign up to request clarification or add additional context in comments.

2 Comments

This is a neat solution for XML, but unfortunately MOXy can't seem to handle this properly when marshalling to JSON in a JAX-RS annotated method. Somehow the List<JAXBElement> triggers marshalling to a JSON array, so the result looks something like this: { "root": { "key1": ["value1"], "key2": ["value2"], "key3": ["value3"] } } I'd be grateful if someone has an idea how to fix this.
Very nice solutions for marshalling only. Unmarshalling does not work with this setup.
1

Note: a marshal/unmarshal example for Map instance can be found here: Dynamic tag names with JAXB.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.