JSONObject as a member variable in POJO not recogn

2019-01-20 17:48发布

问题:

I'm building a RESTful web service. I've been locked in a situation where I'm not able to proceed. I've a DAO (a POJO) that has a JSONObject as a member variable. When I try to make a POST call from client (Postman or user-defined javascript) and try to debug, the value gathered in the getter of the JSONObject is empty ({}) whereas the other members of the class obtain their appropriate values. I've tried annotating the JSONObject and its getter with @XmlElement, @JsonProperty and so on.. Nothing worked.

The class looks like :

package org.somepackage

import javax.xml.bind.annotation.XmlRootElement;

    import org.codehaus.jackson.annotate.JsonAutoDetect;
    import org.codehaus.jackson.annotate.JsonProperty;
    import org.json.JSONObject;


    @XmlRootElement

    public class someClass {
        private String someID;
        private String someName;

        private JSONObject someJsonObject;


        public someClass () {

        }
        public someClass (String id, String name,
                JSONObject jsonObj) {
            someID=id;
            someName=name;
            someJsonObject=jsonObj;

        }
        public String getSomeID() {
            return someID;
        }
        public void setSomeID(String id) {
            this.SomeID= id;
        }
        public String getSomeName() {
            return someName;
        }
        public void setSomeName(String name) {
            this.someName= name;
        }

        public JSONObject getSomeJsonObject() {
            return someJsonObject;
        }
        public void setSomeJsonObject(JSONObject jsonObj) {
            this.someJsonObject= jsonObj;
        }
    }

I appreciate your help! Thanks.


EDIT

Example JSON

{ 
  "name": "ABCD", 
  "ID": "P63784433", 
  "theJSON":{
    "string":"foo",
    "number":5,
    "array":[1,2,3],
    "object":{
      "property":"value‌​",
      "subobj":{
        "arr":["foo","ha"],
        "numero":1
      }
    }
  }
}

DEPENDENCY web.xml dependency on Jackson

                        <dependency>
                                <groupId>org.codehaus.jackson</groupId>
                                <artifactId>jackson-mapper-asl</artifactId>
                                <version>1.9.2</version>
                        </dependency>

                        <dependency>
                                <groupId>org.codehaus.jackson</groupId>
                                <artifactId>jackson-core-asl</artifactId>
                                <version>1.9.2</version>
                        </dependency>

                         <dependency>
                                <groupId>org.codehaus.jackson</groupId>
                                <artifactId>jackson-jaxrs</artifactId>
                                <version>1.9.2</version>
                         </dependency>

                         <dependency>
                                <groupId>org.codehaus.jackson</groupId>
                                <artifactId>jackson-xc</artifactId>
                                <version>1.9.2</version>
                        </dependency>

                        <dependency>
                                <groupId>com.fasterxml.jackson.core</groupId>
                                <artifactId>jackson-core</artifactId>
                                <version>2.6.2</version>
                        </dependency>

RESOURCES AND PROVIDER REGISTER through web.xml

    <!-- Register JAX-RS Application -->
   <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>my.package.MyApplication</param-value>
    </init-param>

    <!-- Register resources and providers under my.package. -->
   <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>my.package</param-value>
    </init-param>

    <!-- Register custom provider  -->
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>my.package.mapper.ObjectMapperProvider</param-value>
    </init-param>`

MyApplication.java

`@ApplicationPath("/")
public class MyApplication extends ResourceConfig {

public MyApplication() {
    // Register resources and providers using package-scanning.
    packages("my.package");

    register(ObjectMapperProvider.class);


}`

回答1:

The problem is that Jackson doesn't know how to create the JSONObject (at least not without some help). Jackson mainly handle basic type and POJOs. If you want to be able to handle JSONObject (assuming this is the object from org.json), you can add the jackson-datatype-json-org for the Jackson support.

Below is a complete test. Here are the dependencies I used to test

<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20141113</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.16</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-json-org</artifactId>
  <version>2.3.2</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
    <version>2.16</version>
    <scope>test</scope>
</dependency>

Note: The Jackson version I am using for jackson-datatype-json-org is the same Jackson version used by jersey-media-json-jackson 2.16. If you are using a different version of this jersey jackson, you will need to make sure the version of Jackson it pulls in is the same version of jackson-datatype-json-org you are using. This way we are not mixing Jackson versions.

Here's the test using Jersey Test Framework

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.json.JSONObject;

import org.junit.Test;
import static junit.framework.Assert.*;

/**
 *
 * @author Paul Samsotha
 */
public class JsonOrgTest extends JerseyTest {

    public static class Model {
        public String firstName;
        public String lastName;
        public JSONObject other;
        // should br private with correct getters and setters
    }

    @Path("model")
    public static class ModelResource {

        @POST
        @Consumes(MediaType.APPLICATION_JSON)
        @Produces(MediaType.APPLICATION_JSON)
        public Response post(Model model) {
            return Response.ok(model).build();
        }
    }

    @Provider
    public static class ObjectMapperProvider implements ContextResolver<ObjectMapper> {

        private final ObjectMapper mapper;

        public ObjectMapperProvider() {
            mapper = new ObjectMapper();
            mapper.registerModule(new JsonOrgModule());
        }

        @Override
        public ObjectMapper getContext(Class<?> type) {
            return mapper;
        }
    }

    @Override
    public ResourceConfig configure() {
        return new ResourceConfig(ModelResource.class)
                .register(ObjectMapperProvider.class)
                .register(JacksonFeature.class);
    }

    @Override
    public void configureClient(ClientConfig config) {
        config.register(JacksonFeature.class);
        config.register(ObjectMapperProvider.class);
    }

    @Test
    public void should_return_org_json_data() {
        final String json 
                = "{\n"
                + "  \"firstName\": \"pee\",\n"
                + "  \"lastName\": \"skillet\",\n"
                + "  \"other\": {\n"
                + "    \"age\": 100,\n"
                + "    \"birthday\": \"yesterday\"\n"
                + "  }\n"
                + "}";
        Response response = target("model").request().post(Entity.json(json));
        if (response.getStatus() != 200) {
            System.out.println(response.getStatus() + ": " + response.readEntity(String.class));
            fail("should return data and 200");
        } else {
            Model model = response.readEntity(Model.class);
            JSONObject other = model.other;
            System.out.println(other.toString());
            assertEquals("pee", model.firstName);
            assertEquals("skillet", model.lastName);
            assertEquals(100, other.getInt("age"));
            assertEquals("yesterday", other.getString("birthday"));
        }
    }
}

What you should also do is get rid of all the Jackson dependencies you have in your comment above. You only need one dependency for Jackson JSON support.

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.16</version>
</dependency>

Also notice the ObjectMapperProvider in the test. You will need to this to register the JsonOrgModule with the ObjectMapper in order for Jackson to be able to handle JSONObject. This is important. If you don't have the ContextResolver, the above example will fail.