0

I'm trying to implement this, where

<SomeXml>
   <SomeData>...</SomeData>
   <InputData>
       <Param key="key1" value="value1" />
       <Param key="key2" value="value2" />
   </InputData>
   <OutputData>
       <Param key="key3" value="value3" />
   </OutputData>
</SomeXml>

becomes

public class SomeXml {
    private SomeData someData;
    private Map<String, String> inputData;
    private Map<String, String> outputData;
}

Where the inputData map has (key1, value1), (key2, value2) and the outputData map has (key3, value3).

Here is what I have written;

@NoArgsConstructor
@AllArgsConstructor
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement
public class MapElement {

    @XmlAttribute(name = "key')
    private String key;

    @XmlAttribute(name = "value")
    private String value;
}

@NoArgsConstructor
public class MapAdapter extends XmlAdapter<MapElement[], Map<String, String>> {

    public MapElement[] marshal(Map<String, String> args) throws Exception {
        MapElement[] mapElements = new mapElement[args.size()];
        int i = 0;
        for (Map.Entry<String, String> entry : args.entrySet()) {
            mapElements[i++] = new MapElement(entry.getKey(), entry.getValue());
        }

        return mapElements;
    }

    public Map<String, String> unmarshal(MapElement[] args) throws Exception {
        Map<String, String> m = new TreeMap<>();
        for (MapElement elem : args) {
            m.put(elem.getKey(), elem.getValue());
        }
        return m;
    }
}    

@NoArgsConstructor
@AllArgsConstructor
@XmlAccessorType(XmlAccessorType.FIELD)
@XmlRootElement
public class SomeXml {

    @XmlElement
    private SomeData someData;

    @XmlJavaAdapter(MapAdapter.class)
    @XmlElement(name = "InputData")
    private Map<String, String> inputData;

    @XmlJavaAdapter(MapAdapter.class)
    @XmlElement(name = "OutputData")
    private Map<String, String> outputData;
}

From what I have managed to determine, the InputData and OutputData maps are nonnull, so they are being created, but when checking the length of the args of the MapAdapter.unmarshal function, it is zero, implying that I'm not able to read in the tagged information properly. Any help would be appreciated.

cmitch
  • 233
  • 1
  • 10

1 Answers1

2

In your posted code your SomeXml class looks very incomplete, because it lacks the needed JAXB annotations. I don't know if these annotations are also missing in your real code or if you only forgot to write them in your post here. Anyway, the annotations should look like this:

@Getter
@Setter
@XmlRootElement(name = "SomeXml")
@XmlAccessorType(XmlAccessType.FIELD)
public class SomeXml {
    @XmlElement(name = "SomeData")
    private SomeData someData;
    
    @XmlElement(name = "InputData")
    @XmlJavaTypeAdapter(MapAdapter.class)
    private Map<String, String> inputData;

    @XmlElement(name = "OutputData")
    @XmlJavaTypeAdapter(MapAdapter.class)
    private Map<String, String> outputData;
}

My first recommendation is:
Begin with debugging the marshalling part, because this is much easier than debugging the unmarshalling part. And by doing so you will find and solve some problems in your code which would also spoil your unmarshalling.

I did this with the following test-code

SomeXml someXml = new SomeXml();
someXml.setSomeData(new SomeData());
Map<String, String> inputData = new HashMap<>();
inputData.put("key1", "value1");
inputData.put("key2", "value2");
someXml.setInputData(inputData);
Map<String, String> outputData = new HashMap<>();
outputData.put("key3", "value3");
outputData.put("key4", "value4");
someXml.setOutputData(outputData);
    
JAXBContext context = JAXBContext.newInstance(SomeXml.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(someXml, System.out);

and got the following XML output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SomeXml>
    <SomeData/>
    <InputData>
        <item key="key1" value="value1"/>
        <item key="key2" value="value2"/>
    </InputData>
    <OutputData>
        <item key="key3" value="value3"/>
        <item key="key4" value="value4"/>
    </OutputData>
</SomeXml>

Here you see that your MapAdapter class does a quite good job on marshalling. But there is a problem. The marshalled XML output has <item .../> elements instead of the wanted <Param .../> elements. From this you can also conclude: Your unmarshalling code didn't process the <Param ... /> elements, because it expected <item ... /> elements instead.

So you have two options:

  • Change your XML input to use <item> instead of <Param>.
  • Tell JAXB that your MapElement class corresponds to <Param> XML elements. I leave it to you to work out the details of this.
Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49
  • I wrote my SomeXml class twice in this post, once to show what I wanted and once showing how I implemented it. I haven't tested this result as we figured out a different way to approach this, but it looks accurate so thank you! – cmitch May 19 '21 at 18:49