Optimal JAXB XJC code generation and optimal XML s

2019-06-21 16:33发布

问题:

There have been a lot of similar questions which focus on one aspect to optimize but each solution had an ugly downside.

Suppose I want to develop an XML schema (XSD) which allows the following documents and want to generate classes using XJC:

<Catalogue>
    <Book>...</Book>
    <Journal>...</Journal>
    <Book>...</Book>
    ...
</Catalogue>

The schema should model the type hierarchy (Book and Journal are subclasses of Publication). Naturally, this should also be the case for the generated Java classes.

I tried the following approches which all have a major issue:

1.) modeling Catalogue to contain a xsd:choice of all possible subtypes.

<xsd:complexType name="Catalogue">
    <xsd:choice maxOccurs="unbounded">
        <xsd:element ref="Book" />
        <xsd:element ref="Magazine" />
    </xsd:choice>
</xsd:complexType>

<xsd:element name="Publication" abstract="true" type="Publication" />
<xsd:element name="Book" type="Book"/>
<xsd:element name="Magazine" type="Magazine"/>

<xsd:complexType name="Publication">
    <xsd:sequence></xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Book">
    <xsd:complexContent>
        <xsd:extension base="Publication">
        </xsd:extension>
    </xsd:complexContent>
</xsd:complexType>
...

The problem here is that I have to mention all possible subtypes in the choice element which could be a lot in a real application. A minor issue is that, although the Catalogue attribute has the correct type List<Publication> it has an ugly name bookAndMagazine. Because of the redundant schema definition, not an option!

2.) modeling Catalogue to contain a xsd:sequence of the parent class

<xsd:complexType name="Catalogue">
    <xsd:choice maxOccurs="unbounded">
        <xsd:element ref="Publication" maxOccurs="unbounded"/>
    </xsd:choice>
</xsd:complexType>

This does only work if the XML documents are formulated like <Publication xsi:type="Book"...> . Thus, not an option!

3.) Use substitutionGroup like mentioned here http://www.xfront.com/ElementHierarchy.html

<xsd:complexType name="Catalogue">
    <xsd:choice maxOccurs="unbounded">
        <xsd:element ref="Publication" maxOccurs="unbounded"/>
    </xsd:choice>
</xsd:complexType>

<xsd:element name="Publication" abstract="true" type="Publication" />
<xsd:element name="Book" type="Book" substitutionGroup="Publication"/>
<xsd:element name="Magazine" type="Magazine" substitutionGroup="Publication"/>

<xsd:complexType name="Publication">
    <xsd:sequence></xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Book">
    <xsd:complexContent>
        <xsd:extension base="Publication">
        </xsd:extension>
    </xsd:complexContent>
</xsd:complexType>
...

Here, the code generation is the issue because the inner element of Catalogue is mapped to List<JaxbElement<? extends Publication>> rather than List<Publication> . Hence, also this isn't an option.

How to bring all my objectives together?:

  • canonical, non-redundant schema which models inheritance (such as in 2.) or 3.))
  • simple and clean Java classes generated from this schema and which models inheritance (like in 2.) and partly in 1.) )
  • clean XML documents (not like in 2.) )
  • usage of standard JAXB and preferable not much binding metadata

And if there is no solution which matches all these objectives, which one would you prefer?

回答1:

I would suggest using something similar to your first option as I have never seen a cleaner solution to this problem.

XSD

<xs:element name="Publications" minOccurs="0">
  <xs:complexType>
    <xs:sequence>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="Magazine" type="magazine"/>
        <xs:element name="Book" type="book"/>
      </xs:choice>
    </xs:sequence>
  </xs:complexType>
</xs:element>

 <xs:complexType name="magazine">
  <xs:complexContent>
   <xs:extension base="publication">
    <xs:sequence>
      <xs:element name="issueName" type="xs:string" minOccurs="0"/>
    </xs:sequence>
  </xs:extension>
 </xs:complexContent>
</xs:complexType>

<xs:complexType name="publication">
  <xs:sequence>
    <xs:element name="name" type="xs:string" minOccurs="0"/>
  </xs:sequence>
</xs:complexType>

<xs:complexType name="book">
  <xs:complexContent>
    <xs:extension base="publication">
      <xs:sequence>
        <xs:element name="title" type="xs:string" minOccurs="0"/>
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

JAVA This is the Java code I used to generate the above XSD.

@XmlElements({
  @XmlElement(name="Magazine", type=Magazine.class),
  @XmlElement(name="Book", type=Book.class)
})
@XmlElementWrapper(name="Publications")
public List<Publication> publications;


回答2:

If you can start from the code (instead of the XSD) and are OK with using MOXY check out this blog post. It uses method 3 without the JAXBElement wrappers. I'd love a way to do this starting with the XSD, but I haven't found it yet.