3

I have some data in an XML-file which i want to unmarshal with JAXB into MyHashMap. MyObject has an String name, which is the key in my HashMap.

To prevent writing the key/name information twice into my XML-File (once as the name of MyObject and once as the key of MyHashMap), so i added setter and getter for an ArrayList, which add/read the data into/out of MyHashMap.

@XmlRootElement
public class MyHashMap extends HashMap<String, MyObject> implements Serializable {

    public MyHashMap() {
        super();
    }

    @XmlElement(name = "MyObject")
    public void setMyObjectsArrayList(ArrayList<MyObject> MyObjectList) {
        for (MyObject myObject : MyObjectList) {
            this.put(myObject.getName(), myObject);
        }
    }

    public ArrayList<MyObject> getMyObjectsArrayList() {
        if (this.isEmpty()) { // Added this to get my setter called
            return null;
        }
        ArrayList<MyObject> MyObjectList = new ArrayList<MyObject>();
        MyObjectList.addAll(this.values());
        return MyObjectList;
    }
}

It worked fine in Java 7 (according to the andswer of this because of a bug ), but doesnt in Java 8. In Java 8 JAXB gets the ListObject and adds the Elemts instead of using the setter.

So i added a "return null" if the list is empty. Apparently JAXB then sets an empty list and adds the Elements afterwards which obviously does not work with my code. Is there a possibility to tell JAXB to add the Elements to the list and call the setter only then?

Community
  • 1
  • 1
deetz
  • 491
  • 1
  • 7
  • 20

1 Answers1

1

You can use Map as JAXB would marshal and unmarshal it. (Cf. https://jaxb.java.net/tutorial/section_6_2_1-A-Survey-Of-JAXB-Annotations.html) To avoid the repetition of name, annotate it with @XmlTransient in MyObject.

Another approach would be to use an Adapter; you'd have to wrap your Map into a parent element. See https://jaxb.java.net/tutorial/section_6_2_9-Type-Adapters-XmlJavaTypeAdapter.html

Yet another possibility is to use a simpler approach with a slightly different implementation of the root element class. It will let you use MyHashMap very much like a Map and avoids all dirty tricks, although the accessors are a little more inconvenient to call. Of course, you may also implement Map and do it clean and convenient, but that's more code to write.

@XmlRootElement
public class MyHashMap {
    private boolean dirty = false;
    private Map<String,MyObject> str2obj;
    private List<MyObject> myObjects = new ArrayList<>();

    @XmlElement(name = "MyObject")
    List<MyObject> getMyObjects(){
        dirty = true;
        return myObjects();
    }

    // variant 1: expose str2obj
    @XmlTransient
    public Map<String,MyObject> getStr2Obj(){
        if( dirty ) rebuild();
        return str2obj;
    }

    private void rebuild(){
        str2obj.clear();
        for( MyObject myObject: myObjects ){
            str2obj.put(mmyObject.getName(), myObject );
        }
        dirty = false;
    }

    // variant 2: delegate put, get, and other methods
    // use the same approach
}
laune
  • 31,114
  • 3
  • 29
  • 42