I've encountered a problem with Jackson serialization for my LoginResp object.
It shows me
Infinite recursion (StackOverflowError) (through reference chain: java.util.logging.LogManager["systemContext"]->java.util.logging.SystemLoggerContext["namedLoggers"]->java.util.Hashtable["global"]->java.util.logging.LoggerWeakRef["referent"]->java.util.logging.Logger["manager"]->java.util.logging.LogManager["systemContext"]->java.util.logging.SystemLoggerContext["namedLoggers"]->java.util.Hashtable["global"]->java.util.logging.LoggerWeakRef["referent"]->java.util.logging.Logger["manager"]->java.util.logging.LogManager["systemContext"]->java.util.logging.SystemLoggerContext["namedLoggers"]->java.util.Hashtable["global"]->java.util.logging.LoggerWeakRef["referent"]-.....
I'm trying to replicate the same scenario and using the same code , as mentioned in this link. But getting the above error. Please help, as I simply wants to write the value of my "resp" bean, to my "someJsonString" variable. Thanks in advance.
These are my dependencies
<properties>
<servlet-api-version>3.1.0</servlet-api-version>
<spring-webmvc-version>4.2.4.RELEASE</spring-webmvc-version>
<jackson-version>2.6.4</jackson-version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet-api-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-webmvc-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
My LoginResp class
package my.beans.resp;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
#Component
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class LoginResp {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
My REST controller
package my.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import my.beans.req.LoginReqB;
import my.beans.resp.LoginResp;
#RestController
#RequestMapping(path = "/login")
public class LoginC {
#Autowired
private LoginResp resp;
#RequestMapping(method = RequestMethod.POST)
public String authenticateUserLogin(#RequestBody LoginReqB request) {
resp.setUsername("abc");
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
SimpleFilterProvider filterP = new SimpleFilterProvider().addFilter("loginResp",
SimpleBeanPropertyFilter.filterOutAllExcept("username"));
mapper.setFilterProvider(filterP);
String someJsonString = "";
try {
someJsonString = mapper.writeValueAsString(resp);
} catch (JsonProcessingException e) {
System.out.println(e.getMessage());
}
return someJsonString;
}
}
I solved it.
Observation: Jackson is not able to convert/process a proxy object at line
someJsonString = mapper.writeValueAsString(resp); in try block.
Solution: As proxy objects are wrapped around the actual object. I'm accessing the actual object behind the proxy using the function mentioned here.
Effect: Now, no need to customize my ObjectMapper, i.e.
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
SimpleFilterProvider filterP = new SimpleFilterProvider().addFilter("loginResp",
SimpleBeanPropertyFilter.filterOutAllExcept("username"));
mapper.setFilterProvider(filterP);
is no longer required.
First of all, don't use System.out.println method on the server side. Always use some logging utility such as log4j slf4j or any other one you might know. However, this is very unlikely a reason for your problem. Judging by the error, you have some circular reference with your logging configuration.Set a break point in your controller to see if you get there before the error occurs or the error occurs even before you attempt to invoke your method. In any case my guess is that you have logging configuration problem and your problem is the result of that and not of your code, which seems to be OK at first glance except System.out.println usage.
Related
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);
}`
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.
I have below implementation of Feign Load balancer which is working with spring cloud Hoxtan SR6 dependencies.
import feign.auth.BasicAuthRequestInterceptor;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.annotation.Bean;
public class ClientConfig {
#Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor(
#Value("${username}") String username,
#Value("${password}") String password) {
return new BasicAuthRequestInterceptor(username, password);
}
#Autowired
private CachingSpringLoadBalancerFactory cachingFactory;
#Autowired
private SpringClientFactory clientFactory;
#Value("${keystore.location}")
private String keyStoreLocation;
#Value("${keystore.secPhase}")
private String keyPassword;
#Bean
public Client feignClient() {
SslUtils.KeystoreConfig truststoreConfig = SslUtils.KeystoreConfig.builder().type("JKS").location(keyStoreLocation).password(keyPassword).build();
SocketFactory factory = new SocketFactory(() -> SslUtils.newContext(null, truststoreConfig));
NoopHostnameVerifier verifier = new NoopHostnameVerifier();
Client.Default client = new Client.Default(factory, verifier);
return new LoadBalancerFeignClient(client, cachingFactory, clientFactory);
}
}
I tried to upgrade spring cloud version to 2020.0.0. I noticed below packages no longer available.
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
How can I change the current implementation? or what dependency will provide these packages?
I was experiencing the same issue.
Finally, I solve the error by adding the bellow dependency.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
I have a Spring Boot project with next dependency of Springfox:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
And I have my Interface:
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import springfox.documentation.annotations.ApiIgnore;
#RestController
#RequestMapping(value = "/cache")
#ApiIgnore
#Api(hidden = true)
public interface CacheController {
#RequestMapping(
value = "clear/",
method = RequestMethod.GET,
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE}
)
#ApiOperation(value = "", hidden = true)
ResponseEntity<String> clearToken();
}
The annotations #ApiIgnore and #Api(hidden = true) (I've tested them separately and they don't work either.) haven't effects to hide the documentation. It only works if the annotation is over the method, but I would like hide them all since I have other endpoints I'd like to hide.
Some ideas?
EDIT:
This is my Swagger configuration:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#Configuration
#EnableSwagger2
public class SwaggerConfig {
public static String API_KEY_NAME;
#Bean
public Docket apiDocumentation() {
List<ResponseMessage> errorList = this.defineResponseMessages();
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("my.package.rest"))
.paths(PathSelectors.any())
.build()
.useDefaultResponseMessages(true)
.globalResponseMessage(RequestMethod.GET, errorList)
.securitySchemes(Arrays.asList(this.apiKey()))
.securityContexts(Arrays.asList(this.securityContext()))
.apiInfo(this.apiInfo());
}
#Value("${server.security.apiKeyName}")
public void setApiKeyName(final String apiKeyName) {
SwaggerConfig.API_KEY_NAME = apiKeyName;
}
private ApiKey apiKey() {
return new ApiKey("apiKey", API_KEY_NAME, "header");
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.any()).build();
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(new SecurityReference("apiKey", authorizationScopes));
}
private List<ResponseMessage> defineResponseMessages() {
List<ResponseMessage> errorList = new ArrayList<ResponseMessage>();
ResponseMessage responseMessage = new ResponseMessageBuilder()
.code(HttpStatus.INTERNAL_SERVER_ERROR.value())
.message(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase())
.build();
errorList.add(responseMessage);
responseMessage = new ResponseMessageBuilder()
.code(HttpStatus.UNAUTHORIZED.value())
.message(HttpStatus.UNAUTHORIZED.getReasonPhrase())
.build();
errorList.add(responseMessage);
responseMessage = new ResponseMessageBuilder()
.code(HttpStatus.NOT_FOUND.value())
.message(HttpStatus.NOT_FOUND.getReasonPhrase())
.build();
errorList.add(responseMessage);
return errorList;
}
private ApiInfo apiInfo() {
ApiInfoBuilder apiInfoBuilder = new ApiInfoBuilder();
return apiInfoBuilder
.title("My API")
.description("Description")
.version("1.0.0 Beta")
.build();
}
}
You have added the #ApiIgnore annotation on an interface. It looks like, this annotation doesn't work when added on an interface. (I really don't understand why #Api works on an interface and #ApiIgnore don't. 😕)
Add the annotation directly to your controller class. This should solve your problem.
The hidden property on the #Api annotation doesn't work currently. (See this GitHub issue.)
For Springdoc see: How to hide endpoints from OpenAPI documentation with Springdoc
For OpenAPI3 and SpringBoot:
I used #Hidden annotation on a method of a controller.
It seems to work both at method level and controller level.
#Hidden annotation was imported from using:
import io.swagger.v3.oas.annotations;
One more way is to use #ApiOperation(hidden = true)
This can be used at controller/handler level method.
E.g.
#RestController
public HomeController{
#ApiOperation(value = "<Your Message>", hidden = true)
public String getMessage(#RequestParam(value = "msg") final String msg){
return msg;
}
}
The scenario where we want to hide only a particular method(s) from the class. For swagger.v3 there is an annotation with name Hidden in io.swagger.core.v3:swagger-annotations:2.0.10 jar. Methods to be hidden can be annotated with Hidden annotation as shown below. The below method shows the method with DELETE operation which needs to be hidden from the swagger documentation.
#DELETE
#Hidden
public void deleteList(int id) {
//code goes here.
}
Another different great way is to define the visible paths on the SpringFox Configuration
#Configuration
#EnableSwagger2
public class SpringFoxConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(Predicates.or(PathSelectors.ant("/rtm/**"), PathSelectors.ant("/appview/**")))
.build().apiInfo(apiEndPointsInfo());
}
}
This way you can define the visible paths centraly and avoid puting swagger annotations on many controllers.
Another option is to just remove the #Api completely, and your controller and its methods shouldn't be picked up by swagger.
I have troubles getting JSON to work. ObjectMapper cannot be resolved. Library is imported correctly.
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.json.JSONException;
import net.sf.json.util.*;
import com.fasterxml.jackson.*;
public class Json {
private static final String jsonFilePath = "C:\\Users\\Juergen\\Desktop\\filesForExamples\\mapExample.json";
public static void objectToJSON(HashMap<String, Mat> map) {
//Map<String, Object> mapObject = new HashMap<String, Object>();
ObjectMapper mapper = new ObjectMapper();
try {
objectMapper.writeValue(new File(jsonFilePath), map);
} catch (IOException e) {
e.printStackTrace();
}
}
}
If you are using **maven** project then add the following in the `POM.xml`
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.1</version>
</dependency>
But if you are using a **simple java** project then you need to add the following jars in your class path:
jackson-core-2.1.X,
jackson-databind-2.1.X
Add import com.fasterxml.jackson.databind.ObjectMapper; to your project.
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);
}`
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.