Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB @XmlAdapter: Map -> List adapter? (marshall only)

I have a Map<String, String>.
The first idea everyone has is to convert it to a List<Pair<String,String>> (Pair being a custom class).

I've tried a @XmlAdapter like this:

public class MapPropertiesAdapter extends XmlAdapter<List<Property>, Map<String,String>> { ... }

But Eclipse MOXy, the JAXB impl I use, ended up with a ClassCastException - "can't convert HashMap to Collection".

Is this conversion supported by JAXB? Or did I overlook some documentation part which explains why it isn't?

PS: I wanted to get XML like this:

<properties>
    <property name="protocol"/>
    <property name="marshaller"/>
    <property name="unmarshaller"/>
    <property name="timeout"/>
    ...
</properties>

I got it, only had to use an intermediate class. Also described at Handle NPE in XMLCompositeObjectMappingNodeValue.marshalSingleValue( XMLCompositeObjectMappingNodeValue.java:161)

like image 553
Ondra Žižka Avatar asked Jun 10 '13 12:06

Ondra Žižka


People also ask

What is JAXB_ formatted_ output?

JAXB_FORMATTED_OUTPUT. The name of the property used to specify whether or not the marshalled XML data is formatted with linefeeds and indentation. static String.

What is Java marshal?

In the Java-related RFC 2713, marshalling is used when serialising objects for remote invocation. An object that is marshalled records the state of the original object and it contains the codebase (codebase here refers to a list of URLs where the object code can be loaded from, and not source code).

What is XmlAdapter?

This abstract class defines methods for adapting a bound type to a value type or vice versa. The methods are invoked by the JAXB binding framework during marshaling and unmarshalling: XmlAdapter.

What is the use of Marshaller in Java?

Marshalling – Converting Java Object to XML Marshalling gives a client application the ability to convert a JAXB-derived Java object tree into XML data. By default, the Marshaller uses UTF-8 encoding when generating XML data. Next, we will generate XML files from Java objects.


1 Answers

Instead of adapting the Map to a List, you should adapt it to an object that has a List property.

XmlAdapter (MapPropertiesAdapter)

import java.util.*;
import java.util.Map.Entry;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class MapPropertiesAdapter extends XmlAdapter<MapPropertiesAdapter.AdaptedProperties, Map<String, String>>{

    public static class AdaptedProperties {
        public List<Property> property = new ArrayList<Property>();
    }

    public static class Property {
        @XmlAttribute
        public String name;

        @XmlValue
        public String value;
    }

    @Override
    public Map<String, String> unmarshal(AdaptedProperties adaptedProperties) throws Exception {
        if(null == adaptedProperties) {
            return null;
        }
        Map<String, String> map = new HashMap<String, String>(adaptedProperties.property.size());
        for(Property property : adaptedProperties.property) {
            map.put(property.name, property.value);
        }
        return map;
    }

    @Override
    public AdaptedProperties marshal(Map<String, String> map) throws Exception {
        if(null == map) {
            return null;
        }
        AdaptedProperties adaptedProperties = new AdaptedProperties();
        for(Entry<String,String> entry : map.entrySet()) {
            Property property = new Property();
            property.name = entry.getKey();
            property.value = entry.getValue();
            adaptedProperties.property.add(property);
        }
        return adaptedProperties;
    }

}

Domain Model (Root)

Below is a model object with a Map property. The @XmlJavaTypeAdapter annotation is used to specify the XmlAdapter.

import java.util.Map;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

    @XmlJavaTypeAdapter(MapPropertiesAdapter.class)
    private Map<String, String> properties;

}

Demo

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum17024050/input.xml");
        Root root = (Root) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }

}

input.xml/Output

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <properties>
        <property name="A">a</property>
        <property name="B">b</property>
    </properties>
</root>
like image 103
bdoughan Avatar answered Oct 02 '22 12:10

bdoughan