Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove intermediate class when generating code from schema

Tags:

xml

jaxb

xsd

xjc

Let's assume we defined a collection type in XSD as

<xs:complexType name="Foos">
  <xs:sequence>
    <xs:element name="foo" minOccurs="0" maxOccurs="unbounded">
      <xs:complexType>
        <xs:all>
          <xs:element name="bar" type="xs:string"/>
          <xs:element name="baz" type="xs:string"/>
        </xs:all>
      </xs:complexType>
    </xs:element>
  </xs:sequence>
</xs:complexType>

When generating Java code from it using XJC the type roughly translates to

public class Foos {
  public List<Foos.Foo> getFoos();

  public static class Foo {
    public String getBar();
    public String getBaz();
  }
}

As the collection type is part of some other type such as the root of the document, client code of the generated code looks somewhat like this

for(Foo foo : document.getFoos().getFoos())
{
  //do something
}

Is there any way to make the client code less ugly without writing a wrapper manually?

It should look like this

for(Foo foo : document.getFoos())
{
  //do something
}

Thanks

like image 773
mibollma Avatar asked Aug 15 '13 06:08

mibollma


1 Answers

UPDATE

There are XJC plug-ins that people have written to generate the @XmlElementWrapper annotation instead of having the extra wrapper class.

  • https://github.com/dmak/jaxb-xew-plugin

ORIGINAL ANSWER

Alternative you could create the class with the @XmlElementWrapper yourself and have the generated classes reference it by doing the following:

Document

You could handcraft your own Document class to get the desired behaviour. You can get the behaviour you are looking for by leveraging the @XmlElementWrapper annotation to get a grouping element.

package forum18247182;

import java.util.*;
import javax.xml.bind.annotation.*;

public class Document {

    private List<Foos.Foo> foos = new ArrayList<Foos.Foo>();

    @XmlElementWrapper
    @XmlElement(name="foo")
    public List<Foos.Foo> getFoos() {
        return foos;
    }

}

XML Schema (schema.xsd)

Here is an expanded XML schema based on your fragment that I will use.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.example.org/schema" 
    xmlns="http://www.example.org/schema"
    elementFormDefault="qualified">

    <xs:element name="document" type="Document"/>

    <xs:complexType name="Document">
        <xs:sequence>
            <xs:element name="foos" type="Foos"/>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="Foos">
        <xs:sequence>
            <xs:element name="foo" minOccurs="0" maxOccurs="unbounded">
                <xs:complexType>
                    <xs:all>
                        <xs:element name="bar" type="xs:string" />
                        <xs:element name="baz" type="xs:string" />
                    </xs:all>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:complexType>

</xs:schema>

Leverage Existing Class When Generating Java Model from XML Schema (binding.xml)

We will use an external binding file to indicate that during class generation we wish to use our existing class for the complex type called Document.

<jxb:bindings 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    version="2.1">

    <jxb:bindings schemaLocation="schema.xsd">
        <jxb:bindings node="//xs:complexType[@name='Document']">
            <jxb:class ref="forum18247182.Document"/>
        </jxb:bindings>
    </jxb:bindings>
</jxb:bindings>

XJC Call

We will use the -b option to specify our binding file. We will also use the -p option to force the package name of the generated classes to match that of our Document class. We could also have made the package name of our Document class match the package name that results from generating classes from the XML schema.

xjc -b binding.xml -p forum18247182 schema.xsd

Demo Code

package forum18247182;

import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import forum18247182.Foos.Foo;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance("forum18247182");

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        StreamSource xml = new StreamSource("src/forum18247182/input.xml");
        Document document = unmarshaller.unmarshal(xml, Document.class).getValue();

        for(Foo foo : document.getFoos())
        {
          System.out.println(foo);
        }
    }

}

Output

Below is the output from running the demo code:

forum18247182.Foos$Foo@51f3336e
forum18247182.Foos$Foo@35b5a4ca
like image 147
bdoughan Avatar answered Nov 16 '22 03:11

bdoughan