4

I have an XML representing a hierarchical DOM where each element is an <element ...> with zero or more children each. Each <element> has a ton of attributes and I don't want to litter the Element class with all those attributes, given it has a bunch of its own methods too.

The first draft of Element class was as follows. This works flawlessly:

class Element {

    @XmlAttribute private String name;
    @XmlAttribute private String bounds;
    // A whole bunch of other attributes
    @XmlElement(name = "element") List<Element> children;
    // A whole bunch of other methods

}

I tried to improve this by:

class Element {

    @XmlPath(".") private Attributes attributes;
    @XmlElement(name = "element") List<Element> children;
    // A whole bunch of other methods

}

class Attributes {

    @XmlAttribute private String name;
    @XmlAttribute private String bounds;
    // A whole bunch of other attributes

}

This seems to work fine, however, it actually messes up data upon closer inspection. If I input the following XML:

<element name="Hello" bounds="[0,0][1,1]">
  <element name="World" bounds="[1,1][2,2]">
    <element name="Testing" bounds="[2,2][3,3]">
      <element name="One two three" bounds="[3,3][4,4]" />
    </element>
  </element>
</element>

The unmarshalled object has the following structure/properties:

+ [Element]
   - name = "World"
   - bounds = "[1,1][2,2]"
   + children[0]
      - name = "Testing"
      - bounds = "[2,2][3,3]"
      + children[0]
        - name = "One two three"
        - bounds = "[3,3][4,4]"
        + children[0]
          - name = "One two three"
          - bounds = "[3,3][4,4]"
          - children = null

My assumption was that XPath(".") would "lift" the attributes of the Attributes class to the parent Element class. But in fact it is lifting those attributes two levels upwards.

When I construct the Element hierarchy manually, then try to marshall it, the resulting XML is just fine. It is just the unmarshalling that is producing the incorrect object.

Am I using XPath incorrectly here? I have a working solution by including all attributes directly within the Element class, but I just wanted to group those attributes into a separate class and marshall/unmarshall them to the container Element class.

Thanks! Asim

AweSIM
  • 1,651
  • 4
  • 18
  • 37
  • Looks like a MOXy bug. – Olivier Nov 09 '21 at 08:13
  • @Olivier Sure seems like that to me. But I doubt it should be so, given that Moxy has been in the works for so long and surely someone would've stumbled across this long before I did. :( – AweSIM Nov 09 '21 at 14:44

1 Answers1

1

How about using @XmlAnyAttribute with a Map<String,Object>. That should put all attributes that are not accounted for into the map.

class Element {
    @XmlAnyAttribute private Map<String,String> attributes;
    @XmlElement(name = "element") List<Element> children;
}

Using it like element.attributes.get("name)

Alternatively, you could extend a common Attributes class. These would look in the XML just as if they are in the Element class.

public class Attributes {

    @XmlAttribute
    private String name;

    @XmlAttribute
    private String bounds;
}

and

class Element extends Attributes {

    @XmlElement(name = "element")
    List<Element> children;
}
3Fish
  • 640
  • 3
  • 18
  • You're right that it works, and I am already using it to bunch all unaccounted for attributes in a map. But I lose type-safety this way which is really important to me. So I want to have strongly-typed members that represent the attributes I'm most interested in. :( – AweSIM Nov 09 '21 at 22:58
  • You could also use extension, see my answer above. But besides that, I don't think it is possible. Also, I don't know if you actually should separate it, as the separation in two classes is clearly not what the XML actually represents. – 3Fish Nov 10 '21 at 10:42