ymajoros
ymajoros

Reputation: 2728

JAXB mapping for a map

Using JAXB, how to map <entry key="childResources"> below?

I tried mapping it to a Map, to a list of @XmlRootElement anotated classes and other ways, without success.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<map>    
  <entry key="extraProperties">        
    <map>            
      <entry key="methods">                
        <list>                    
          <map>                        
            <entry key="name" value="GET" />
          </map>                    
          <map />                    
          <map>                        
            <entry key="name" value="POST" />                        
            <entry key="messageParameters">                            
              <map>                                
                <entry key="id">                                    
                  <map>                                        
                    <entry key="acceptableValues" value="" />                                        
                    <entry key="optional" value="false" />                                        
                    <entry key="defaultValue" value="" />                                        
                    <entry key="type" value="string" />
                  </map>                                
                </entry>                                
                <entry key="enabled">                                    
                  <map>                                        
                    <entry key="acceptableValues" value="" />                                        
                    <entry key="optional" value="true" />                                        
                    <entry key="defaultValue" value="true" />                                        
                    <entry key="type" value="boolean" />
                  </map>                                
                </entry>                                
                <entry key="factoryclass">                                    
                  <map>                                        
                    <entry key="acceptableValues" value="" />                                        
                    <entry key="optional" value="false" />                                        
                    <entry key="defaultValue" value="" />                                        
                    <entry key="type" value="string" />
                  </map>                                
                </entry>                                
                <entry key="description">                                    
                  <map>                                        
                    <entry key="acceptableValues" value="" />                                        
                    <entry key="optional" value="true" />                                        
                    <entry key="defaultValue" value="" />                                        
                    <entry key="type" value="string" />
                  </map>                                
                </entry>                                
                <entry key="target">                                    
                  <map>                                        
                    <entry key="acceptableValues" value="" />                                        
                    <entry key="optional" value="true" />                                        
                    <entry key="defaultValue" value="server" />                                        
                    <entry key="type" value="string" />
                  </map>                                
                </entry>                                
                <entry key="property">                                    
                  <map>                                        
                    <entry key="acceptableValues" value="" />                                        
                    <entry key="optional" value="true" />                                        
                    <entry key="defaultValue" value="" />                                        
                    <entry key="type" value="string" />
                  </map>                                
                </entry>                                
                <entry key="restype">                                    
                  <map>                                        
                    <entry key="acceptableValues" value="" />                                        
                    <entry key="optional" value="false" />                                        
                    <entry key="defaultValue" value="" />                                        
                    <entry key="type" value="string" />
                  </map>                                
                </entry>                            
              </map>                        
            </entry>                    
          </map>                
        </list>            
      </entry>            
      <entry key="commands">                
        <list />
      </entry>            
      <entry key="childResources">                
        <map>                    
          <entry key="ab/cd" value="http://localhost:4848/management/domain/resources/custom-resource/ab%2Fcd" />                    
          <entry key="xx" value="http://localhost:4848/management/domain/resources/xx" />
        </map>            
      </entry>        
    </map>    
  </entry>    
  <entry key="message" value="" />    
  <entry key="exit_code" value="SUCCESS" />    
  <entry key="command" value="custom-resource" />
</map>

Upvotes: 3

Views: 5851

Answers (2)

Flavio Cysne
Flavio Cysne

Reputation: 1456

The java.util.Map interface has no provider to marshal/unmarshal its instances. You'll have to write a class that extends XmlAdapter and annotate the java.util.Map attribute with @XmlJavaTypeAdapter(MyMapAdapter.class).

After a while on Google looking for jax-rs java.util.map, I ended up with the codes bellow. In my case I have named my adapter as XmlStringMapAdapter instead of MyMapAdapter.

ServiceDefinition.java

@XmlRootElement(name = "entry")
@XmlAccessorType(XmlAccessType.FIELD)
public class ServiceDefinition {

    @XmlAttribute
    private String key;

    @XmlJavaTypeAdapter(XmlStringMapAdapter.class)
    private Map<String, String> map = new HashMap<String, String>();

    // getters & setters
}

EntryElement.java

public class EntryElement {

    @XmlAttribute public String key;
    @XmlAttribute public String value;

    public EntryElement() {}

    public EntryElement(String key, String value) {
        this.key = key;
        this.value = value;
    }
}

MapElement.java

@XmlRootElement(name = "map")
public class MapElement {

    @XmlElement(name = "entry")
    public List<EntryElement> entries = new ArrayList<EntryElement>();

    public void addEntry(String key, String value) {
        entries.add(new EntryElement(key, value));
    }

}

XmlStringMapAdapter.java

public class XmlStringMapAdapter extends XmlAdapter<MapElement, Map<String, String>> {

    @Override
    public MapElement marshal(Map<String, String> v) throws Exception {

        if (v == null || v.isEmpty()) {return null;}

        MapElement map = new MapElement();

        for (String key : v.keySet()) {
            map.addEntry(key, v.get(key));
        }

        return map;
    }

    @Override
    public Map<String, String> unmarshal(MapElement v) throws Exception {
        if (v == null) {return null;}

        Map<String, String> map = new HashMap<String, String>(v.entries.size());

        for(EntryElement entry: v.entries) {
            map.put(entry.key, entry.value);
        }

        return map;
    }

}

And bellow is a class that I used to test the new map adapter.

ServiceResouce.java

@Path("service")
public class ServiceResource {

    @Context
    private UriInfo uriInfo;

    @GET
    @Path("definition")
    @Produces("application/xml")
    public Response getDefinition() {
        ServiceDefinition def = new ServiceDefinition();
        UriBuilder b = uriInfo.getBaseUriBuilder();

        def.setKey("childResources");
        def.getMap().put("ab/cd", b.path("ab/cd").build(this).getPath());
        def.getMap().put("xx", b.path("xx").build(this).getPath());

        return Response.ok(def).build();
    }
}

Output from the resource above is as shown bellow.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<entry key="childResources">
    <map>
        <entry key="ab/cd" value="http://localhost:8080/management/resources/ab/cd" />
        <entry key="xx" value="http://localhost:8080/management/resources/xx" />
    </map>
</entry>

Upvotes: 8

Artem Oboturov
Artem Oboturov

Reputation: 4385

Look at the XSD for your document:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="map" type="mapType"/>
  <xs:complexType name="entryType" mixed="true">
    <xs:sequence>
      <xs:element type="mapType" name="map" minOccurs="0"/>
      <xs:element type="listType" name="list" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute type="xs:string" name="key" use="optional"/>
    <xs:attribute type="xs:string" name="value" use="optional"/>
  </xs:complexType>
  <xs:complexType name="listType" mixed="true">
    <xs:sequence>
      <xs:element type="mapType" name="map" maxOccurs="unbounded" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="mapType" mixed="true">
    <xs:sequence>
      <xs:element type="entryType" name="entry" maxOccurs="unbounded" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

Compare it to mapping you did for your classes. Would be nice to see you classes and annotations it question formulation.

You can generate model with mappings from this schema with many different utilities for IDEs like Eclipse and Intellij or via CLI.

Upvotes: 0

Related Questions