Search code examples
jsonweb-servicesjax-rswildfly

Issue with JAX-RS JSON unmarshalling in wildfly 26


I am creating a war file to be deployed on wildfly, that serves xml/json api web services;

I am using xjc to generate classes to be used by the webservice as follows

    <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxb2-maven-plugin</artifactId>
                <version>2.5.0</version>
                <executions>
                    <execution>
                        <id>xjc</id>
                        <goals>
                            <goal>xjc</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <xjbSources>
                        src/main/resources/binding
                    </xjbSources>
                    <sources>
                        <source>src/main/resources/xsd</source>
                    </sources>
                    <packageName>org.exp.binding</packageName>
                    <outputDirectory>target/generated-sources</outputDirectory>
                    <clearOutputDir>true</clearOutputDir>
                </configuration>
            </plugin>

I am using a simple global binding:

<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema"
    
    xsi:schemaLocation="../xsd/schema.xsd" jaxb:version="1.0">

 <jaxb:globalBindings>
        <xjc:simple/>
    </jaxb:globalBindings>
</jaxb:bindings>

I have a simple schema, which has an object with a property that is derived from a complext type and a property with a list the same type as follows

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">


    <xs:element name="topLevelObject">
    
        <xs:complexType>
            <xs:sequence>
                <xs:element name="singleAttributes" type="attribute" maxOccurs="1"/>
                <xs:element name="multiAttributes" type="attribute" maxOccurs="10"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>


    <xs:complexType name="attribute">
        <xs:simpleContent>
            <xs:extension base="xs:string">
                <xs:attribute name="id" type="xs:string" />
                <xs:attribute name="name" use="required"
                    type="xs:string" />
            </xs:extension>
        </xs:simpleContent>
    </xs:complexType>

<xs:element name="attribute" type="attribute" />

</xs:schema>

This generates an object as follows

//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.2 
// See <a href="https://javaee.github.io/jaxb-v2/">https://javaee.github.io/jaxb-v2/</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2023.06.29 at 08:41:14 AM CEST 
//


package org.wipo.exp.binding;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for anonymous complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType&gt;
 *   &lt;complexContent&gt;
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
 *       &lt;sequence&gt;
 *         &lt;element name="singleAttributes" type="{}attribute"/&gt;
 *         &lt;element name="multiAttributes" type="{}attribute" maxOccurs="10"/&gt;
 *       &lt;/sequence&gt;
 *     &lt;/restriction&gt;
 *   &lt;/complexContent&gt;
 * &lt;/complexType&gt;
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "singleAttributes",
    "multiAttributes"
})
@XmlRootElement(name = "topLevelObject")
public class TopLevelObject {

    @XmlElement(required = true)
    protected Attribute singleAttributes;
    @XmlElement(required = true)
    protected List<Attribute> multiAttributes;

    /**
     * Gets the value of the singleAttributes property.
     * 
     * @return
     *     possible object is
     *     {@link Attribute }
     *     
     */
    public Attribute getSingleAttributes() {
        return singleAttributes;
    }

    /**
     * Sets the value of the singleAttributes property.
     * 
     * @param value
     *     allowed object is
     *     {@link Attribute }
     *     
     */
    public void setSingleAttributes(Attribute value) {
        this.singleAttributes = value;
    }

    /**
     * Gets the value of the multiAttributes property.
     * 
     * <p>
     * This accessor method returns a reference to the live list,
     * not a snapshot. Therefore any modification you make to the
     * returned list will be present inside the JAXB object.
     * This is why there is not a <CODE>set</CODE> method for the multiAttributes property.
     * 
     * <p>
     * For example, to add a new item, do as follows:
     * <pre>
     *    getMultiAttributes().add(newItem);
     * </pre>
     * 
     * 
     * <p>
     * Objects of the following type(s) are allowed in the list
     * {@link Attribute }
     * 
     * 
     */
    public List<Attribute> getMultiAttributes() {
        if (multiAttributes == null) {
            multiAttributes = new ArrayList<Attribute>();
        }
        return this.multiAttributes;
    }

}

I have a web service that reads the object and then returns it:

package org.exp.rest;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

import org.wipo.exp.binding.TopLevelObject;


@Path("/applications")
@Produces({ "application/xml", "application/json" })
@Consumes({ "application/xml", "application/json" })
public class ApplicationEndpoint {

    @POST
    @Path("/test")
    public TopLevelObject  test(TopLevelObject tlo) {
        return tlo;
    }
    
    
}

Now all this works fine on Wildfly versions from 11 to 23

curl -X POST -d '{"singleAttributes":{"name":"abc","value":"1234"}, "multiAttributes":[{"name":"abc","value":"1234"}]}' -H "Content-Type: application/json" -v localhost:8080/exp-0.0.1-SNAPSHOT/rest/applications/test
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying ::1:8080...
* TCP_NODELAY set
*   Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /exp-0.0.1-SNAPSHOT/rest/applications/test HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.65.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 101
>
* upload completely sent off: 101 out of 101 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Type: application/xml;charset=UTF-8
< Content-Length: 190
< Date: Thu, 29 Jun 2023 06:21:25 GMT
<
* Connection #0 to host localhost left intact
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><topLevelObject><singleAttributes name="abc">1234</singleAttributes><multiAttributes name="abc">1234</multiAttributes></topLevelObject> 

But from wildfly 25+ the list is not unmarshalled

curl -X POST -d '{"singleAttributes":{"name":"abc","value":"1234"}, "multiAttributes":[{"attribute":{"name":"abc","value":"1234"}}]}' -H "Content-Type: application/json" -v localhost:8080/exp-0.0.1-SNAPSHOT/rest/applications/test
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying ::1:8080...
* TCP_NODELAY set
*   Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /exp-0.0.1-SNAPSHOT/rest/applications/test HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.65.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 115
>
* upload completely sent off: 115 out of 115 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Type: application/xml;charset=UTF-8
< Content-Length: 140
< Date: Thu, 29 Jun 2023 09:18:44 GMT
<
* Connection #0 to host localhost left intact
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><topLevelObject><singleAttributes name="abc">1234</singleAttributes></topLevelObject>  

Is there some specific config i should add to wildfly versions beyond 25 to unmarshall lists in json correctly?

If I send the message in xml it works correctly, this behavior seems limited to json.


Solution

  • In WildFly 24 there was a switch made to switch from using Jackson by default for JSON processing to Jakarta JSON Binding processing. The latter does not support the JAXB annotations.

    The simple solution is to simply add a setter for the multiAttributes. Something like:

    public void setMultiAttributes(final Collection<Attribute> attributes) {
        if (attributes != null) {
            this.multiAttributes = List.copyOf(attributes);
        }
    }
    

    You could also enable Jackson if you'd prefer as well. Note that documentation is for WildFly 28, but it should work the same for WildFly 26.