Reputation: 31
We've upgraded from JAXB 3.0.2 to 4.0.5 and serializing a field on a set of objects has started failing. I rolled back versions and it seams to first appear in JAXB 4.0.0.
The java objects are generated from a WSDL. The problem is that the Parent object has a field ID and ChildB has a field Id. When I try to serialize ChildA, the parent ID field is never serialized. If I remove the Id field from the definition of ChildB, then I can serialize ID (from Parent) when serializing ChildA.
If I roll back the JAXB version to 3.0.2, it has no problem serializing ID (Parent) when Id (ChildB) is defined. Or, if I am using JAXB 4.0.5 and I delete Id from ChildB, it works fine as well.
Why does a field existing in ChildB prevent ChildA from serializing a field on their shared Parent? Is there extra annotations or config I need to apply? I would like to avoid changing the WSDL if possible.
Parent.ID serialized - either JAXB 3.0.2 or ChildB.Id deleted:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:UpdateRequest xmlns:ns2="http://example.org/wsdl/testAPI">
<ns2:Objects xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:ChildA">
<ns2:ID>1234</ns2:ID>
<ns2:Name>test-name</ns2:Name>
</ns2:Objects>
</ns2:UpdateRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Parent.ID NOT serialized - JAXB 4.0.5 and ChildB.Id defined:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:UpdateRequest xmlns:ns2="http://example.org/wsdl/testAPI">
<ns2:Objects xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:ChildA">
<ns2:Name>test-name</ns2:Name>
</ns2:Objects>
</ns2:UpdateRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Test Code:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>xml-test</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jakarta.version>3.0.2</jakarta.version>
<jakarta.xml.bind-api.version>4.0.2</jakarta.xml.bind-api.version>
<wss4j.version>1.6.19</wss4j.version>
<jaxb.version>4.0.5</jaxb.version>
<!-- <jaxb.version>3.0.2</jaxb.version> -->
<spring-ws-security.version>4.0.11</spring-ws-security.version>
<saaj-impl.version>3.0.4</saaj-impl.version>
<spring.boot.version>3.2.2</spring.boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>jakarta.xml.soap</groupId>
<artifactId>jakarta.xml.soap-api</artifactId>
<version>${jakarta.version}</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>${jakarta.xml.bind-api.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.ws</groupId>
<artifactId>jaxws-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.jws</groupId>
<artifactId>javax.jws-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
<version>${saaj-impl.version}</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>${spring-ws-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-security</artifactId>
<version>${spring-ws-security.version}</version>
</dependency>
<dependency>
<groupId>org.apache.ws.security</groupId>
<artifactId>wss4j</artifactId>
<version>${wss4j.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-test</artifactId>
<scope>test</scope>
<version>4.0.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.1</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-commons</artifactId>
<version>1.10.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<scope>compile</scope>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb</groupId>
<artifactId>jaxb-maven-plugin</artifactId>
<version>4.0.6</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>${jaxb.version}</version>
</dependency>
</dependencies>
<configuration>
<generatePackage>org.example</generatePackage>
<schemaDirectory>src/main/resources</schemaDirectory>
<schemaIncludes>
<include>*.wsdl</include>
</schemaIncludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
src/main/resources/test.wsdl
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://example.org/wsdl/testAPI" targetNamespace="http://example.org/wsdl/testAPI">
<types>
<schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://example.org/wsdl/testAPI" version="1.0">
<complexType name="Parent">
<sequence>
<element name="ID" type="xsd:int" minOccurs="0" maxOccurs="1"/>
</sequence>
</complexType>
<complexType name="ChildA">
<complexContent>
<extension base="tns:Parent">
<sequence>
<element name="Name" minOccurs="0" type="xsd:string"/>
</sequence>
</extension>
</complexContent>
</complexType>
<complexType name="Result">
<sequence>
<element name="StatusCode" type="xsd:string"/>
<element name="StatusMessage" type="xsd:string"/>
</sequence>
</complexType>
<element name="UpdateRequest">
<complexType>
<sequence>
<element name="Objects" type="tns:Parent" minOccurs="1" maxOccurs="unbounded"/>
</sequence>
</complexType>
</element>
<complexType name="UpdateResult">
<complexContent>
<extension base="tns:Result">
<sequence>
<element name="Object" type="tns:Parent"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="UpdateResponse">
<complexType>
<sequence>
<element name="Results" type="tns:UpdateResult" minOccurs="1" maxOccurs="unbounded"/>
</sequence>
</complexType>
</element>
<complexType name="ChildB">
<complexContent>
<extension base="tns:Parent">
<sequence>
<element name="Id" type="xsd:int" nillable="true" minOccurs="0" maxOccurs="1"/>
</sequence>
</extension>
</complexContent>
</complexType>
</schema>
</types>
<message name="updateRequest">
<part element="tns:UpdateRequest" name="parameters"/>
</message>
<message name="updateResponse">
<part element="tns:UpdateResponse" name="parameters"/>
</message>
<portType name="Soap">
<operation name="Update">
<documentation>Update objects</documentation>
<input message="tns:updateRequest"/>
<output message="tns:updateResponse"/>
</operation>
</portType>
<binding name="SoapBinding" type="tns:Soap">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="Update">
<soap:operation soapAction="Update"/>
<input>
<soap:body parts="parameters" use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="testAPI">
<documentation>test API</documentation>
<port binding="tns:SoapBinding" name="Soap">
<soap:address location="https://example.org/Service.asmx"/>
</port>
</service>
</definitions>
src/test/java/org.example/XmlTest.java:
package org.example;
import com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.ws.support.MarshallingUtils;
import org.example.XmlTest.TestSoapConfiguration;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static org.springframework.ws.test.client.RequestMatchers.soapEnvelope;
import static org.springframework.ws.test.client.ResponseCreators.withSoapEnvelope;
@SpringBootTest(
classes = {
TestApplication.class,
TestSoapConfiguration.class,
},
webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@ExtendWith(SpringExtension.class)
@ExtendWith(MockitoExtension.class)
public class XmlTest {
@Test
public void test() throws IOException {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// this package must match the package in the <generatePackage> specified in pom.xml
marshaller.setContextPath("org.example");
ChildA ca = new ChildA();
ca.setName("test-name");
ca.setID(1234);
UpdateRequest req = new UpdateRequest();
req.getObjects().add(ca);
DefaultMessageContext ctx = new DefaultMessageContext(new SaajSoapMessageFactory(new SOAPMessageFactory1_1Impl()));
WebServiceMessage msg = ctx.getRequest();
MarshallingUtils.marshal(marshaller, req, msg);
ByteArrayOutputStream out = new ByteArrayOutputStream();;
msg.writeTo(out);
String str = out.toString(StandardCharsets.UTF_8);
System.out.println(str);
}
@TestConfiguration
public static class TestSoapConfiguration {
}
src/test/java/org.example/TestApplication.java:
package org.example;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
@EnableConfigurationProperties
public class TestApplication {
public TestApplication() {}
}
Upvotes: 3
Views: 57