The question is about JAXB Map marshalling - there is plenty of examples on how to marhsall a Map into a structure like follows:
<map> <entry> <key> KEY </key> <value> VALUE </value> </entry> <entry> <key> KEY2 </key> <value> VALUE2 </value> </entry> <entry> ... </map>
In fact, this is natively supported by JAXB. What I need, however, is the XML where key is the element name, and value is its content:
<map> <key> VALUE </key> <key2> VALUE2 </key2> ... </map>
I didn't succeed implementing my Map adapter the way it is recommended by JAXB developers (https://jaxb.dev.java.net/guide/Mapping_your_favorite_class.html), as I need, he - dynamic attribute name :)
Is there any solution for that?
P.S. Currently I have to create a dedicated container class for each typical set of key-value pairs I want to marshall to XML - it works, but I have to create way too many of these helper containers.
In JAXB, marshalling involves parsing an XML content object tree and writing out an XML document that is an accurate representation of the original XML document, and is valid with respect the source schema. JAXB can marshal XML data to XML documents, SAX content handlers, and DOM nodes.
To unmarshal an xml string into a JAXB object, you will need to create an Unmarshaller from the JAXBContext, then call the unmarshal() method with a source/reader and the expected root object.
If you are using the default JAXB implementation provided with Java 6 or later, you can configure the namespace prefixes by extending the NamespacePrefixMapper and setting a property to tell the marshaller to use your extension.
There may be a valid reason why you want to do this, but generating this kind of XML is generally best avoided. Why? Because it means that the XML elements of your map are dependent on the runtime contents of your map. And since XML is usually used as an external interface or interface layer this is not desirable. Let me explain.
The Xml Schema (xsd) defines the interface contract of your XML documents. In addition to being able to generate code from the XSD, JAXB can also generate the XML schema for you from the code. This allows you to restrict the data exchanged over the interface to the pre-agreed structures defined in the XSD.
In the default case for a Map<String, String>
, the generated XSD will restrict the map element to contain multiple entry elements each of which must contain one xs:string
key and one xs:string
value. That's a pretty clear interface contract.
What you describe is that you want the xml map to contain elements whose name will be determined by the content of the map at runtime. Then the generated XSD can only specify that the map must contain a list of elements whose type is unknown at compile time. This is something that you should generally avoid when defining an interface contract.
To achieve a strict contract in this case, you should use an enumerated type as the key of the map instead of a String. E.g.
public enum KeyType { KEY, KEY2; } @XmlJavaTypeAdapter(MapAdapter.class) Map<KeyType , String> mapProperty;
That way the keys which you want to become elements in XML are known at compile time so JAXB should be able to generate a schema that would restrict the elements of map to elements using one of the predefined keys KEY or KEY2.
On the other hand, if you wish to simplify the default generated structure
<map> <entry> <key>KEY</key> <value>VALUE</value> </entry> <entry> <key>KEY2</key> <value>VALUE2</value> </entry> </map>
To something simpler like this
<map> <item key="KEY" value="VALUE"/> <item key="KEY2" value="VALUE2"/> </map>
You can use a MapAdapter that converts the Map to an array of MapElements as follows:
class MapElements { @XmlAttribute public String key; @XmlAttribute public String value; private MapElements() { } //Required by JAXB public MapElements(String key, String value) { this.key = key; this.value = value; } } public class MapAdapter extends XmlAdapter<MapElements[], Map<String, String>> { public MapAdapter() { } public MapElements[] marshal(Map<String, String> arg0) throws Exception { MapElements[] mapElements = new MapElements[arg0.size()]; int i = 0; for (Map.Entry<String, String> entry : arg0.entrySet()) mapElements[i++] = new MapElements(entry.getKey(), entry.getValue()); return mapElements; } public Map<String, String> unmarshal(MapElements[] arg0) throws Exception { Map<String, String> r = new TreeMap<String, String>(); for (MapElements mapelement : arg0) r.put(mapelement.key, mapelement.value); return r; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With