Question

I am using xjc to generate Java classes from the XML schema and the following is an excerpt of the XSD.

<xs:element name="NameInfo">
  <xs:complexType>
    <xs:sequence>
      <xs:choice>
        <xs:element ref="UnstructuredName"/> <!-- This line -->
        <xs:sequence>
          <xs:element ref="StructuredName"/>
          <xs:element ref="UnstructuredName" minOccurs="0"/> <!-- and this line! -->
        </xs:sequence>
      </xs:choice>
      <xs:element ref="SomethingElse" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

For the most part the generated classes are fine but for the above block I would get something like:

public List<Object> getContent() {
  if (content == null) {
    content = new ArrayList<Object>();
  }
  return this.content;
}

with the following comment above it:

* You are getting this "catch-all" property because of the following reason: 
* The field name "UnstructuredName" is used by two different parts of a schema. See: 
* line XXXX of file:FILE.xsd
* line XXXX of file:FILE.xsd
* To get rid of this property, apply a property customization to one 
* of both of the following declarations to change their names: 
* Gets the value of the content property.

I have placed a comment at the end of the two line in question.

At the moment, I don't think it will be easy to change the schema since this was decided between vendors and I would not want to go this route (if possible) as it will slow down progress quite a bit.

I searched and have found this page, is the external customization what I want to do? I have been mostly working with the generated classes so I'm not entirely familiar with the process that generates these classes. A simple example of the "property customization" would be great! Alternative method of generating the Java classes would be fine as long as the schema can still be used.

EDIT: I should clarify that the two UnstructuredName are indeed the same element.

Was it helpful?

Solution

The essential problem here is that you have a <xs:sequence> consisting of an <xs:choice>, which translates in Java to "a List of things". Java's type structure isn't flexible enough to represent this any better.

A binding customisation might help you, but in this case, I suspect not, since I can't see a better way to represent this information.

An alternative technique I've used in the past is to pass the schema through a simple XSLT transformation first, re-arranging the components into something more JAXB-friendly, while still permitting the same structures the documents will have in reality. This way, you can "change" the schema without changing the original.

OTHER TIPS

You can also use a binding customization called <xjc:simple />:

<?xml version="1.0" encoding="UTF-8"?>
<jxb:bindings xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
              xmlns:xs="http://www.w3.org/2001/XMLSchema"
              xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
              jxb:version="2.0">
        <jxb:globalBindings>
            <xjc:simple />
        </jxb:globalBindings>
</jxb:bindings>

Please however be adviced that this is vendor specific (who uses something else than XJC though ;) )

More Info here

I have had the same issue. I switched to xmlbeans and axis. XMLBeans can compile your schema without issue and without headache. JaxB can't handle this. In order to make JaxB handle this, you could change your schema a little.

  <xs:sequence>
          <xs:choice>
    <!-- changed the following line -->
            <xs:element name="UnstructuredTop" type="UnstructuredName"/> 
    <!-- end of change -->
            <xs:sequence>
              <xs:element ref="StructuredName"/>
              <xs:element ref="UnstructuredName" minOccurs="0"/>
            </xs:sequence>
          </xs:choice>
          <xs:element ref="SomethingElse" minOccurs="0"/>
    </xs:sequence>

Then JaxB will distinguish the two and not flip out.

However your situation is like my situation. Changing the schema was out of the question. So I went with xmlBeans and axis (which sux).

I created a wrapper class to solve the problem:

List<JAXBElement<?>> contentList = address.getContent();
if (contentList != null && contentList.size() > 0) {
    Address4JaxbMula address4JaxbMula = new Address4JaxbMula(contentList);

... }

...

public static class Address4JaxbMula {
    public CountryCodeType countryCode;
    public AddressFixType addressFix;
    public String addressFree;

    public Address4JaxbMula(List<JAXBElement<?>> contentList) {
        if (contentList != null && contentList.size() > 0) {
            for (JAXBElement<?> content : contentList) {
                Object value = content.getValue();
                if (value.getClass().isAssignableFrom(CountryCodeType.class)) {
                    countryCode = (CountryCodeType) content.getValue();
                } else if (value.getClass().isAssignableFrom(AddressFixType.class)) {
                    addressFix = (AddressFixType) content.getValue();
                } else {
                    addressFree = (String) value;
                }
            }
        }
    }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top