1

I have a fairly complicated data structure that I can't seem to get to unmarshal correctly.

@XmlRootElement
class Tree {
  @XmlID
  private String id;

  @XmlJavaTypeAdapter(type=TreeFooAdapter.class)
  Map<Tree, Foo> fooMap;
}

class Foo {
  @XmlID
  private String id;
}

I have TWO separate trees. Two nodes (one from each tree) can be paired and associated with an instance of Foo. fooMap is used to keep track of what other nodes a given tree node has been paired with, and what instance of Foo results.

The TreeFooAdapter is pretty straightforward, but note that it uses ID refs:

public class TreeFooAdapter extends XmlAdapter<TreeFooAdapter.MapType, Map<Tree, Foo>> {
  public static class MapType {
    public static class MapEntry {
      @XmlAttribute
      @XmlIDREF
      public Tree key;
      @XmlAttribute
      @XmlIDREF
      public Foo value;
    }
    // etc...
  }

  // Standard drill for marshal/unmarshal...
}

THE PROBLEM: Forward references don't work! When unmarshalling, whichever Tree comes first in the XML will have null keys in its fooMap. Since the two trees reference each other, there's no way that I could change the order of the XML to get around this.

I've tried a hack wherein I have a private method to get/set a List<TreeFooMapEntry>, but it produces the same result.

Why is JAXB unable to handle forward ID references when they're contained in a Map or List, and how can I resolve this?

Kricket
  • 4,049
  • 8
  • 33
  • 46

2 Answers2

0

I've found a (horrible, awful, ugly) workaround, inspired by this. I basically generalized the PhoneNumberAdapter class to work with anything "Identifiable", i.e. having a unique ID (i.e., both Foo and Tree). Instead of having an AdaptedPhoneNumber class, I have an ObjectWrapper bean that will either contain the actual object being wrapped (when first encountered), or just its ID (on subsequent encounters). The MapEntry class in TreeFooAdapter, instead of having XmlIDREFs, instead specifies my IdentifiableAdapter as the XmlJavaTypeAdapter.

This creates god-awful, unreadable XML, but at least it works. Still, this seems like a bug in JAXB: after some painful debugging, I still haven't been able to figure out why XmlIDREF doesn't work when inside of an adapted Map class.

Community
  • 1
  • 1
Kricket
  • 4,049
  • 8
  • 33
  • 46
  • ...although, after adding more depth to my unit tests, I see that this doesn't quite work, either... :( – Kricket Aug 31 '12 at 09:38
0

I tried another hack that appears to work this time. The trick involves the following:

  • create a private getter/setter pair that transforms fooMap into TreeFooAdapter.MapType
  • add an XmlID to TreeFooAdapter.MapType, and annotate the getter with XmlIDREF
  • maintain a static Set of instances of TreeFooAdapter.MapType
  • when marshalling, AFTER marshalling all the instances of Tree and Foo, also marshal the Set of instances.

This produces much cleaner XML: the Trees are marshalled with simple IDREFs for their fooMaps, and the end of the XML file is just a long list of TreeFooAdapter.MapTypes, which are themselves just lists of pairs of IDREFs. You have to be careful to clean up the static Set of instances if you marshal/unmarshal more than once.

Kricket
  • 4,049
  • 8
  • 33
  • 46