2

What?

I am using CXF's wsdl2java to generate Java classes from an externally supplied wsdl (TravelItineraryReadRQ.wsdl). When I call the operation, I get a response that I can unmarshal into Java classes, with the exception of one element and its children which default to DOM elements (ElementNSImpl)

What's special about this element?

The element in question (PriceQuote) is of type (PriceQuoteType) defined in the OpenReservation schema as xs:any:

<xsd:complexType name="PriceQuoteType">
  <xsd:choice>
      <xsd:any processContents="strict"/>
    </xsd:choice>
</xsd:complexType>

Why does this happen?

The element that I receive (a PriceQuoteInfo element) belongs to a namespace that is not referenced in this wsdl, so none of the ObjectFactory classes that the JAXBContext has been created with, can unmarshal the element. It therefore ends up as raw DOM elements (ElementNSImpl).

Do I have the schema where the PriceQuoteInfo type is defined?

Yes, I have obtained this, and run wsdl2java on it to generate Java classes for it and appended them to the classpath.

What have I tried?

To enable the PriceQuoteInfo to be unmarshalled independently, I created an external binding file to annotate the PriceQuoteInfo as an XmlRootElement and referenced this file in the maven build:

<jaxb:bindings version="2.1"
               xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               xmlns:annox="http://annox.dev.java.net"
               jaxb:extensionBindingPrefixes="annox">    
    <jaxb:bindings schemaLocation="../PriceQuoteServices_v.3.0.0.xsd" node="/xsd:schema">
       <jaxb:bindings node="xsd:complexType[@name='PriceQuoteInfo.Get.Response']">
            <jaxb:class name="PQSPriceQuoteInfo"/>
            <annox:annotate target="class">
                <annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="PriceQuoteInfo" namespace="http://www.sabre.com/ns/Ticketing/pqs/1.0"/>
            </annox:annotate>
        </jaxb:bindings>
    </jaxb:bindings>
  </jaxb:bindings>

Partial Success

I'm then able to unmarshal PriceQuoteInfo XML independently, using a JAXBContext initialised as the following:

JAXBContext context = JAXBContext.newInstance(PQSPriceQuoteInfo.class)

When unmarshalling PriceQuoteInfo as a child of a TravelItineraryReadRS element, I can also unmarshal it by declaring the JAXBContext as:

  JAXBContext context = JAXBContext.newInstance(TravelItineraryReadRS.class, PQSPriceQuoteInfo.class)

or alternatively, by using their object factories:

  JAXBContext context = JAXBContext.newInstance(com.sabre.services.res.tir.v3_10.ObjectFactory.class, com.sabre.ns.ticketing.pqs.v1_0.ObjectFactory.class)

And what does the unmarshalling code look like?

    InputStream inputStream = MyUnitTest.getResourceAsStream(filename)
    Unmarshaller unmarshaller = context.createUnmarshaller()
    JAXBElement<TravelItineraryReadRS> travelItineraryReadRS = unmarshaller.unmarshal(new StreamSource(inputStream), TravelItineraryReadRS.class)

And this unmarshals some TravelItineraryReadRS XML into Java classes, without any raw DOM objects.

So what's the problem?

When I call this service via CXF / JAX-WS the PriceQuoteInfo unmarshals into raw DOM elements as I haven't found a way of modifying the JAXBContext that it uses to add the missing object factory.

@XmlSeeAlso

The @XmlSeeAlso annotation on the wsdl's generated *PortType interface contains a list of ObjectFactory classes. By replicating and extending the TravelItineraryReadPortType interface and adding com.sabre.ns.ticketing.pqs.v1_0.ObjectFactory to the list in the @XmlSeeAlso annotation, I succeeded in triggering an XML validation error during unmarshalling of some children of the PriceQuoteInfo element:

unexpected element (uri:"http://www.sabre.com/ns/Ticketing/pqs/1.0", local:"ValidatingCarrier")

Although not very elegant it shows that this approach is attempting to unmarshal the PriceQuoteInfo rather than leaving it as DOM objects, but that the XML isn't exactly matching the GetPriceQuote schema I generated. I'll either turn off validation or find a matching schema to resolve that.

The Question

Is it possible to influence the JAXBContext used in a CXF / JAX-WS web service call so as to add the ObjectFactory for PriceQuoteInfo to the generated *PortType?

For example: is there a way of influencing the generated @XmlSeeAlso annotation so that it includes this ObjectFactory, by using external bindings?

roj
  • 1,262
  • 13
  • 27

1 Answers1

3

Had a similar problem. I found no way to influence JAXBContext directly but I was able to inject my ObjectFactory via JaxWsProxyFactoryBean.setProperties() when creating the JAX-WS proxy:

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setProperties(
    Collections.singletonMap(
                "jaxb.additionalContextClasses",
                new Class[{com.mypackage.ObjectFactory.class});

This way I got JAXBElement<T> instead of raw DOM objects (T would be PriceQuoteInfo in your case). Hope that helps.

Al3x3
  • 463
  • 3
  • 9
  • 1
    You have a syntax mistake, correct is: factory.setProperties( Collections.singletonMap( "jaxb.additionalContextClasses", new Class[]{ObjectFactory.class})); – Mauricio Zárate Aug 24 '20 at 21:28