5

I am using JAXB to generate Java code from a couple of XSD files. Then, inside of an OSGi container I am unmarshalling XML files to the generated code. The XSD uses xsd:any element:

<xsd:complexType name="GetReservationRSType">
    <xsd:sequence>
        <xsd:element name="Errors" type="pnrb:Errors.PNRB"
            minOccurs="0" />
        <xsd:choice>
            <xsd:element name="Reservation" type="pnrb:Reservation.PNRB"
                minOccurs="0" />
            <xsd:element name="Content" minOccurs="0">
                <xsd:complexType>
                    <xsd:choice>
                        <xsd:any processContents="lax" />
                    </xsd:choice>
                </xsd:complexType>
            </xsd:element>
        </xsd:choice>
    </xsd:sequence>
</xsd:complexType>

I had several problems with making it working in the production code, but eventually I solved it when I manually added @XmlSeeAlso annotation (@XmlSeeAlso(value = { OTATravelItineraryRS.class }) in the code below):

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "GetReservationRSType", propOrder = {
    "warnings",
    "errors",
    "reservation",
    "content"
})
@XmlSeeAlso({
    GetReservationRS.class
})
public class GetReservationRSType {

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {
        "any"
    })
    @XmlSeeAlso(value = { OTATravelItineraryRS.class })
    public static class Content {
       //  ...
    }
    // ...
}

Is there any way I can force JAXB to automatically add such annotation? For instance, by adding some JAXB bindings configuration option or by modifying XSD files?

EDIT:

My JAXBContext in OSGi env is initialized in the following way (it gets all the generated packages names as parameter) - they are listed with the usage of colon delimiter as it was suggested in several JAXB-related posts:

<bean id="jaxbContext" class="javax.xml.bind.JAXBContext" factory-method="newInstance">
    <constructor-arg index="0" value="com.sabre.webservices.pnrbuilder:com.sabre.webservices.sabrexml._2003._07" />    
</bean>      

I am getting the following exception:

Caused by: javax.xml.bind.MarshalException
 - with linked exception:
[javax.xml.bind.JAXBException: class com.sabre.webservices.sabrexml._2003._07.OTATravelItineraryRS nor any of its super class is known to this context.]
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:318)
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:244)
    at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:105)
    at org.apache.camel.converter.jaxb.FallbackTypeConverter.marshall(FallbackTypeConverter.java:174)
    at org.apache.camel.converter.jaxb.FallbackTypeConverter.convertTo(FallbackTypeConverter.java:88)
    ... 94 more
Caused by: javax.xml.bind.JAXBException: class com.sabre.webservices.sabrexml._2003._07.OTATravelItineraryRS nor any of its super class is known to this context.
    at com.sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:246)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:261)
    at com.sun.xml.bind.v2.runtime.property.SingleReferenceNodeProperty.serializeBody(SingleReferenceNodeProperty.java:113)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:332)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:699)
    at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:152)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:332)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:328)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:593)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:320)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:494)
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:315)
    ... 98 more
Caused by: javax.xml.bind.JAXBException: class com.sabre.webservices.sabrexml._2003._07.OTATravelItineraryRS nor any of its super class is known to this context.
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.java:590)
    at com.sun.xml.bind.v2.runtime.property.SingleReferenceNodeProperty.serializeBody(SingleReferenceNodeProperty.java:105)
    ... 107 more
Lukasz
  • 7,572
  • 4
  • 41
  • 50

2 Answers2

4

I found a satisfying workaround. It uses jaxb annotate plugin. Maybe it's not the perfect solution, but - at least - it prevents me from committing the generated classes into SVN repository. The mentioned plugin simply adds the wanted annotation.

Here is the required configuration in pom.xml:

        <plugin>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>

            <executions>
                <execution>
                    <id>schema-pnr-service</id>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                    <configuration>
                        <schemaDirectory>src/main/resources/xsd</schemaDirectory>
                        <schemaIncludes>
                            <include>GetReservationSTLRQ_v0.01.xsd</include>
                            <include>GetReservationSTLRS_v0.01.xsd</include>
                            <include>OTA_TravelItineraryReadCDI1.0.5RQ.xsd</include>
                            <include>OTA_TravelItineraryReadCDI1.0.5RQRS.xsd</include>
                            <include>OTA_TravelItineraryReadCDI1.0.5RS.xsd</include>
                            <include>OTA_TravelItineraryReadPNR1.0.5RS.xsd</include>
                        </schemaIncludes>
                        <extension>true</extension>
                        <args>
                            <arg>-Xannotate</arg>
                            <arg>-Xannotate-defaultFieldTarget=setter</arg>
                        </args>
                        <plugins>
                            <plugin>
                                <groupId>org.jvnet.jaxb2_commons</groupId>
                                <artifactId>jaxb2-basics-annotate</artifactId>
                            </plugin>
                        </plugins>                            
                    </configuration>
                </execution>
            </executions>
        </plugin>  

And the wanted annotation is configured in in bindings.xjb file that should be in the same directory as *.xsd files - here goes its content:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
                xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
                xmlns:annox="http://annox.dev.java.net" 
                extensionBindingPrefixes="annox" version="2.1">
    <globalBindings typesafeEnumMaxMembers="600"/>

    <bindings schemaLocation="GetReservationSTLRS_v0.01.xsd" node="/xsd:schema">
        <bindings node="xsd:complexType[@name='GetReservationRSType']/xsd:sequence/xsd:choice/xsd:element[@name='Content']/xsd:complexType">
            <annox:annotate target="class">
                <annox:annotate annox:class="javax.xml.bind.annotation.XmlSeeAlso" value="com.sabre.webservices.sabrexml._2003._07.OTATravelItineraryRS" />
            </annox:annotate>           
        </bindings>

    </bindings>    
</bindings>
milijan
  • 387
  • 4
  • 12
Lukasz
  • 7,572
  • 4
  • 41
  • 50
0

Instead of trying to force in an @XmlSeeAlso annotation you should create the JAXBContext on the generated package name. This way you can be sure that all the classes are processed. Since you are in an OSGi environment be use to use one of the methods that take a ClassLoader parameter.

JAXBContext jc = JAXBContext.newInstance("com.example.foo", ObjectFactory.class.getClassLoader());

If there are more than one generated package name, then the context path is : delimited.

JAXBContext jc = JAXBContext.newInstance("com.example.foo:org.example.bar", ObjectFactory.class.getClassLoader());
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • The `ClassLoader` parameter is very important. What type of config file did you post? – bdoughan Jan 16 '13 at 13:26
  • This is standard Spring beans.xml file. The jaxbContext bean is then injected into other bean that actually takes care of unmarshalling. – Lukasz Jan 16 '13 at 13:29
  • @LukaszBaran - I can't seem to find a Spring property that allows you to specify a `ClassLoader`. I suspect Spring isn't including one when it creates the `JAXBContext`. Instead you could include the class you want to reference with `@XmlSeeAlso` in Springs list of classes to be bound. – bdoughan Jan 16 '13 at 13:36
  • well, I have just tried to initialize JaxbContext in Java code exactly as you suggested - unfortunately I keep getting the same exception. – Lukasz Jan 16 '13 at 14:44
  • @LukaszBaran - Sounds like this is more of a Spring issue than a JAXB one. – bdoughan Jan 16 '13 at 16:10
  • I don't think so - without Spring it also looks like. BUT I have found a workaround for this problem - I'll post it as a separate answer. – Lukasz Jan 16 '13 at 16:23
  • @LukaszBaran - You see the same issue in OSGi when Spring isn't present? – bdoughan Jan 16 '13 at 16:48
  • @LukaszBaran - How do you bootstrap your `JAXBContext` in the standalone case? Here is a standalone example I put together: http://stackoverflow.com/questions/12942750/moxy-error-with-karaf/12943115#12943115 – bdoughan Jan 16 '13 at 18:05
  • thank you, Blaise, but I have just posted my workaround which I consider as satisfying for me. – Lukasz Jan 16 '13 at 18:22