3

I'm currently updating a old axis wsclient to a cxf(jaxb databinding) client, now there are differences, how list/array are handled.

Let me explain that on an example:

wsdl

<xsd:complexType name="ArrayOfString">
    <xsd:sequence>
        <xsd:element maxOccurs="unbounded" minOccurs="0" name="string" nillable="true" type="xsd:string"/>
    </xsd:sequence>
</xsd:complexType>

which is in a other complexType

<xsd:complexType name="CustomParameter">
    <xsd:sequence>
        <xsd:element minOccurs="0" name="values" nillable="true" type="tns:ArrayOfString"/>
    </xsd:sequence>
</xsd:complexType>

now when trying to access this attribute in cxf it is needed to additionally get the value from the list-wrapper

CustomParameter.getValues().getString(); // returns List<String>

Axis does afaik unwrap this automatically that you got the array only with

CustomParameter.getValues() // returns String[]

My question is, is this possible to do this in cxf?

A part of my wsdl:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://ws611.webservice.adapters.company.de" xmlns:soapenc11="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://ws611.webservice.adapters.company.de" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"
                  xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://usermanagement.ws611.webservice.company.de"
                  xmlns:ns1="http://ws611.webservice.company.de" xmlns:soapenc12="http://www.w3.org/2003/05/soap-encoding">
    <wsdl:types>
        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://ws611.webservice.company.de">
            <xsd:complexType name="AuthenticationToken">
                <xsd:sequence>
                    <xsd:element minOccurs="0" name="password" nillable="true" type="xsd:string"/>
                    <xsd:element minOccurs="0" name="timestamp" nillable="true" type="xsd:string"/>
                    <xsd:element minOccurs="0" name="username" nillable="true" type="xsd:string"/>
                </xsd:sequence>
                <xsd:anyAttribute/>
            </xsd:complexType>
        </xsd:schema>
        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://ws611.webservice.adapters.company.de">
            <xsd:element name="getAllUsers">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element maxOccurs="1" minOccurs="1" name="in0" nillable="true" type="ns1:AuthenticationToken"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
            <xsd:element name="getAllUsersResponse">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element maxOccurs="1" minOccurs="1" name="out" nillable="true" type="ns2:ArrayOfUser"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
        </xsd:schema>
        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://usermanagement.ws611.webservice.company.de">
            <xsd:complexType name="ArrayOfUser">
                <xsd:sequence>
                    <xsd:element maxOccurs="unbounded" minOccurs="0" name="User" nillable="true" type="ns2:User"/>
                </xsd:sequence>
            </xsd:complexType>
            <xsd:complexType name="User">
                <xsd:sequence>
                    <xsd:element minOccurs="0" name="name" nillable="true" type="xsd:string"/>
                    <xsd:element minOccurs="0" name="groups" nillable="true" type="ns2:ArrayOfGroup"/>
                </xsd:sequence>
                <xsd:anyAttribute/>
            </xsd:complexType>
            <xsd:complexType name="ArrayOfGroup">
                <xsd:sequence>
                    <xsd:element maxOccurs="unbounded" minOccurs="0" name="Group" nillable="true" type="ns2:Group"/>
                </xsd:sequence>
            </xsd:complexType>
            <xsd:complexType name="Group">
                <xsd:sequence>
                    <xsd:element minOccurs="0" name="name" nillable="true" type="xsd:string"/>
                </xsd:sequence>
                <xsd:anyAttribute/>
            </xsd:complexType>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="getAllUsersRequest">
        <wsdl:part name="parameters" element="tns:getAllUsers">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="getAllUsersResponse">
        <wsdl:part name="parameters" element="tns:getAllUsersResponse">
        </wsdl:part>
    </wsdl:message>
    <wsdl:portType name="UserManagementPortType">
        <wsdl:operation name="getAllUsers">
            <wsdl:input name="getAllUsersRequest" message="tns:getAllUsersRequest">
            </wsdl:input>
            <wsdl:output name="getAllUsersResponse" message="tns:getAllUsersResponse">
            </wsdl:output>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="UserManagementHttpBinding" type="tns:UserManagementPortType">
        <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="getAllUsers">
            <wsdlsoap:operation soapAction=""/>
            <wsdl:input name="getAllUsersRequest">
                <wsdlsoap:body use="literal"/>
            </wsdl:input>
            <wsdl:output name="getAllUsersResponse">
                <wsdlsoap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="UserManagement">
        <wsdl:port name="UserManagementHttpPort" binding="tns:UserManagementHttpBinding">
            <wsdlsoap:address location="http://localhost:8080/test/webservice/ws611/UserManagement"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>
jmattheis
  • 10,494
  • 11
  • 46
  • 58

1 Answers1

6

It is possible to get the cxf-codegen-plugin to generate model classes that automatically hide XML element wrapper classes.

There are three things you need to use:

  1. A custom JAXB binding file that suppresses usage of JAXBElement
  2. For any PortTypes, a custom JAXWS binding file that makes sure to use your request and response classes directly
  3. The jaxb-xew-plugin to generate @XmlElementWrapper annotations, which is the JAXB annotation for designating lists as wrapped

Here is what the custom JAXB binding file should look like:

<jaxb:bindings version="2.1" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb">
    <jaxb:globalBindings generateElementProperty="false"/>
</jaxb:bindings>

Here is what the custom JAXWS binding file should look like:

<jaxws:bindings xmlns:jaxws="http://java.sun.com/xml/ns/jaxws">
    <jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
</jaxws:bindings>

Here is a sample working usage of the cxf-codegen-plugin using JAXB and JAXWS binding files and the jaxb-xew-plugin:

<plugin>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-codegen-plugin</artifactId>
    <version>3.1.4</version>
    <executions>
        <execution>
            <id>generate-client</id>
            <phase>generate-sources</phase>
            <configuration>
                <wsdlOptions>
                    <wsdlOption>
                        <wsdl>${basedir}/src/main/resources/sample.wsdl</wsdl>
                        <bindingFiles>
                            <bindingFile>${basedir}/src/main/resources/jaxbBinding.xml</bindingFile>
                            <bindingFile>${basedir}/src/main/resources/jaxwsBinding.xml</bindingFile>
                        </bindingFiles>
                        <extraargs>
                            <extraarg>-xjc-Xxew</extraarg>
                            <extraarg>-xjc-Xxew:summary
                                ${project.build.outputDirectory}/../xew-summary.txt
                            </extraarg>
                            <extraarg>-xjc-Xxew:instantiate lazy</extraarg>
                        </extraargs>
                    </wsdlOption>
                </wsdlOptions>
            </configuration>
            <goals>
                <goal>wsdl2java</goal>
            </goals>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>com.github.jaxb-xew-plugin</groupId>
            <artifactId>jaxb-xew-plugin</artifactId>
            <version>1.6</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-xjc</artifactId>
            <version>2.2.11</version>
        </dependency>
    </dependencies>
</plugin>

This configuration will generate the following on the schema provided in the question:

CustomParameter.getValues() // returns List<String>

Note that this does NOT return a String[]. You could get the cxf-codegen-plugin to use a String[] instead of a List<String> by adding a collectionType="indexed" attribute to your globalBindings, but currently the jaxb-xew-plugin only supports Collection types and not arrays.

Community
  • 1
  • 1
heenenee
  • 19,914
  • 1
  • 60
  • 86
  • I tried this plugin, but the generated code has compilation errors, it can not find ``ArrayOfUser``, I edited my querstion with a wsdl to reproduce, the plugin deletes the ``ArrayOfUser`` but in my *PortType, it is still used. – jmattheis Nov 24 '15 at 07:13
  • @JannisM I updated the answer with instructions on how to specify a JAXWS binding file to ensure correct generation of PortTypes. – heenenee Nov 24 '15 at 07:58
  • But when ``enableWrapperStyle`` is ``false``, then there is again, a wrapping from the ``GetAllUsers`` and ``GetAllUsersResponse``, which looks even worse, then the other one, so there is no other way of having ``enableWrapperStyle=true`` and have no list wrapper classes? – jmattheis Nov 24 '15 at 08:02
  • @JannisM `GetAllUsers` exists either way. Setting `enableWrapperStyle` to `false` changes `UserManagementPortType` to return a `GetAllUsersResponse` with a single `List out`, instead of a `ArrayOfUser` with a single `List user`, so it's not any worse. Do you want `UserManagementPortType.getAllUsers` to directly return a `List` instead? That might not be possible. – heenenee Nov 24 '15 at 08:19
  • It would be ok, if the ``UserManagementPortType#getAllUsers`` would return an ``ArrayOfUsers`` but ``ArrayOf*`` as class attributes should get converted to a ``List``, but this seems to be not configurable or? – jmattheis Nov 24 '15 at 08:29
  • When I let ``enableWrapperStyle=true`` and specify the ``ArrayOf*`` types in the xew control file, then there are no compilation errors but when i try to get something from the webservice I get a ``ClassCastException ArrayList cannot be cast to ArrayOfUser`` – jmattheis Nov 24 '15 at 09:07
  • so please edit you comment, then i accept you answer as correct, i do not want that other user think this will work because it just does not (: – jmattheis Nov 26 '15 at 08:54
  • "If you want UserManagementPortType#getAllUsers to return an ArrayOfUsers, skip what I wrote about the JAXWS binding file, and add a jaxb-xew-plugin control file that specifies /*ArrayOf*/=keep so the ArrayOf classes aren't removed. " thats what you said, and this is wrong as i wrote below it. – jmattheis Nov 27 '15 at 15:52