Exception thrown when posting JSON in JAX-RS app r

2020-03-30 06:09发布

问题:

I have a very simple Java app using JAX-RS, running on WLP 8.5.5.8. When I post JSON that is a composite structure I get this error:

[ERROR ] Error occurred during error handling, give up! Invalid type of value. Type: [java.util.LinkedHashMap] with value: [{d=e}] [ERROR ] SRVE0777E: Exception thrown by application class 'org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage:116' java.lang.RuntimeException: org.apache.cxf.interceptor.Fault: Invalid type of value. Type: [java.util.LinkedHashMap] with value: [{d=e}] at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:116) at [internal classes] Caused by: org.apache.cxf.interceptor.Fault: Invalid type of value. Type: [java.util.LinkedHashMap] with value: [{d=e}] at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:76) ... 1 more Caused by: java.lang.IllegalArgumentException: Invalid type of value. Type: [java.util.LinkedHashMap] with value: [{d=e}] at com.ibm.json.java.JSONObject.put(JSONObject.java:241) ... 1 more

When I do a POST on the resource and the json object contains a property that is either a json array or a json object, the exception is thrown before the function is reached

Example JSON that causes this (if I remove the "c" property it works fine):

{
    "a": "b",
    "c": {
        "d": "e"
    }
}

The same error is thrown if "c" is an array:

{
    "a": "b",
    "c": [
        {"d": "e"},
        {"d": "f"},
        {"d": "g"}
    ]
}

I have not added any libs of my own, all is what is included in WLP. If I run this example on WLP 8.5.5.3 it works fine. Any ideas what I am missing because I find it hard to believe that this simple sample expose a defect?

This is my resource:

RootResouce.java

package com.sample;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.ibm.json.java.JSONObject;

@Path("/hello")
public class RootResource {

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response postTest(JSONObject json) {
        return Response.ok(json).build();
    }
}

This is my app:

package com.sample;

import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;

public class MyApp extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> s = new HashSet<Class<?>>();
        s.add(RootResource.class);
        return s;
    }
}

This is my web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <servlet>
        <description>Simple Test service</description>
        <display-name>sample</display-name>
        <servlet-name>sample</servlet-name>
        <servlet-class>com.ibm.websphere.jaxrs.server.IBMRestServlet</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.sample.MyApp</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <enabled>true</enabled>
        <async-supported>false</async-supported>
    </servlet>

    <servlet-mapping>
        <servlet-name>sample</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <display-name>SimpleTest</display-name>
</web-app>

回答1:

Looking a little further with the code provided, I see this in the exception:

    Caused by: java.lang.IllegalArgumentException: Invalid type of value.  Type: [java.util.LinkedHashMap] with value: [{d=e}]
at com.ibm.json.java.JSONObject.put(JSONObject.java:241)
at org.codehaus.jackson.map.deser.MapDeserializer._readAndBind(MapDeserializer.java:244)
at org.codehaus.jackson.map.deser.MapDeserializer.deserialize(MapDeserializer.java:166)
at org.codehaus.jackson.map.deser.MapDeserializer.deserialize(MapDeserializer.java:24)
at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:1961)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:889)
at org.codehaus.jackson.jaxrs.JacksonJsonProvider.readFrom(JacksonJsonProvider.java:410)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBodyReader(JAXRSUtils.java:1356)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBody(JAXRSUtils.java:1307)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameter(JAXRSUtils.java:847)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameters(JAXRSUtils.java:810)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:255)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:85)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307

Maybe the default json provider changed between jax-rs 1.1 and 2.0? Deserializing request data with Jackson into a json4j object is not going well. Setting the provider like this seems to allow it to work:

    import com.ibm.websphere.jaxrs.providers.json4j.JSON4JArrayProvider;
    import com.ibm.websphere.jaxrs.providers.json4j.JSON4JObjectProvider;

    public class MyApp extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> s = new HashSet<Class<?>>();
        s.add(RootResource.class);
        s.add(JSON4JObjectProvider.class);
        s.add(JSON4JArrayProvider.class);
        return s;
    }