Jackson JSON not working with CXF - json

The JacksonJsonProvider is not working with CXF.
CXF v2.6.0
Jackson v2.1.2 (com.fasterxml.jackson)
RESTClient (for testing)
I do have the provider configured like below in beans.xml.
<bean id="jacksonMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd'T'HH:mm:ss.SSSZ"> </constructor-arg>
</bean>
</property>
</bean>
<bean id="jacksonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider">
<property name="mapper" ref="jacksonMapper" />
</bean>
in jaxrs:server.....>
<jaxrs:providers>
<ref bean="jaxbProvider" />
<ref bean="jacksonProvider" />
</jaxrs:providers>
</jaxrs:server>
The application gets deployed without any issues, it gives good JSON while I give the request as "application/xml" and the response as "application/json".
When I try to give JSON in request by setting Content-Type=application/json I'm facing the 500 Internal Server Error
The request is getting logged in the log file thru CXF-logging.
The request is not at all landing in the service implementation class of my webservice.
The JSON in request body is :
{"SearchOrdersRequest":{"LoginCredentials":{"AppId":"BookStore","Username":"myuser","Password":"abcd1234","SecurityToken":"Vcvx45YilzX1"},"SearchHeader":{"SearchCategory":"Rep","FilterLogic":"1 AND 2","SearchParams":{"Field":"Order Number (s)","Operator":"EQUALS","Values":"600045335"}}}}
Any immediate help is appreciated.

In CXF documentation , you can see where you need to add json provider and include a dependency. But, I still getting errors when I tried to add jackson instead of jettison, after some hours I figured that you need to include one more jackson dependency.
Add JSON provider
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider" />
</jaxrs:providers>
Add dependencies
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.12</version>
</dependency>

As I undertood you, your application produces and consumes xml and json format. So, first of all. Make it sure that your cxf resource endpoint are able to do it.
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
Otherwise your request won't find any resource implementation. (at these line at class level or method level)
Then if this is not enough check out this jackson cxf integration:
<bean id="jsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" />
<bean id="jsonContextResolver" class="net.sf.gazpachoquest.rest.support.JacksonContextResolver" />
Also
<jaxrs:server id="services" address="/">
<jaxrs:providers>
<ref bean="jsonProvider" />
<ref bean="jsonContextResolver" />
</jaxrs:providers>
</jaxrs:server>
The context resolver the class where the mapper is defined:
#Provider
public class JacksonContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper = new ObjectMapper();
public JacksonContextResolver() {
/*
* Register JodaModule to handle Joda DateTime Objects.
* https://github.com/FasterXML/jackson-datatype-jsr310
*/
mapper.registerModule(new JSR310Module());
mapper.setSerializationInclusion(Include.NON_EMPTY);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
}
#Override
public ObjectMapper getContext(Class<?> arg0) {
return mapper;
}
}
And just in case you deploy your application into a j2ee container, you may require a application config class:
#ApplicationPath("/api")
public class ApplicationConfig extends javax.ws.rs.core.Application{
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
// add here your resources
classes.add(JacksonContextResolver.class);
classes.add(JacksonJsonProvider.class);
...
return classes;
}
Hope this help.

Some time ago I had problem with a json request when I had not jettison library in classpath.
Check http://www.javatips.net/blog/2012/02/cxf-restful-tutorial tutorial.

In order to use jackson library by overriding jettison, you need to configure jacksonprovider like following
<bean id="jacksonJsonProvider" class="com.student.CustomJsonProvider"/>
<jaxrs:providers>
<ref bean="jacksonJsonProvider" />
</jaxrs:providers>

In normal circumstances if you deploy to a EE server by simply adding that dependency the provider had to be automatically discovered and used (I tested width TomEE 7):
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.8.6 </version>
</dependency>
But be careful to use the correct version. CFX guide suggests a Jackson JAX-RS provider that is only compatible with Jackson 1 and not for Jackson 2. Look at that question for more details

This is happening because when you are using the jackson library with group com.fasterxml.jackson.jaxrs, the server is expecting the request in following format:
{
"LoginCredentials": {
"AppId" : "BookStore",
"Username":"myuser",
"Password":"abcd1234",
"SecurityToken":"Vcvx45YilzX1"
},
"SearchHeader":{
"SearchCategory":"Rep",
"FilterLogic":"1 AND 2",
"SearchParams":{
"Field":"Order Number (s)",
"Operator":"EQUALS",
"Values":"600045335"
}
}
}
But your client will be sending in following format:
{
"SearchOrdersRequest": {
"LoginCredentials": {
"AppId" : "BookStore",
"Username":"myuser",
"Password":"abcd1234",
"SecurityToken":"Vcvx45YilzX1"
},
"SearchHeader":{
"SearchCategory":"Rep",
"FilterLogic":"1 AND 2",
"SearchParams":{
"Field":"Order Number (s)",
"Operator":"EQUALS",
"Values":"600045335"
}
}
}
}
It seems to be the issue with this fasterxml version of library only. Using the library with group org.codehaus.jackson will fix the issue. Both request and response will be without root element.
So you need to replace the following maven dependency
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.9.3</version>
</dependency>
with
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.13</version>
</dependency>
I checked that the library mentioned in the cxf docs (https://cxf.apache.org/docs/jax-rs-data-bindings.html#JAX-RSDataBindings-Jackson) is also the one with com.fasterxml.jackson.jaxrs but if you check the classes that are mentioned there in beans are from org.codehaus.jackson.jaxrs. So I think this is a mistake in docs and needs correction.

Related

getContext() method of CustomContextResolver is not called by Jackson

I am struggling with this issue for days now and have no clue how to solve this. Any quick help will be grateful.
I need to convert LocalDate from JSON string which I am receiving from REST service build using apache CXF and jackson. I wrote custom ContextResolver and registered JavaTimeModule in Mapper object.
When I run the application, default constructor is called, that means it has been loaded, but getContext() method which returns ObjectMapper never gets called.
I have registered same ContextResolver in server and client side.
All dependencies are in place(jackson databind, core, annotation, datatype-jsr310).
I am able to fetch JSON response when I hit REST URI directly in browser. Issue comes when I call same URI annotated method from client code
Below is my client code.
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
#Provider //makes this bean a Provider
public class LocalDateObjectMapperContextResolver implements ContextResolver<ObjectMapper>{
private final ObjectMapper MAPPER;
public LocalDateObjectMapperContextResolver() {
MAPPER = new ObjectMapper();
MAPPER.registerModule(new JavaTimeModule());
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return MAPPER;
}
}
<jaxrs:client id="testclient"
serviceClass="package1.RESTService"
username="abc"
password="abc"
address="$serviceURL">
<jaxrs:features>
<bean class="org.apache.cxf.transport.common.gzip.GZIPFeature"/>
<cxf:logging/>
</jaxrs:features>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
<bean class="mypackage.LocalDateObjectMapperContextResolver"/>
</jaxrs:providers>
</jaxrs:client>
Same way, This contextResolver is registered on server side also under
<jaxrs:server>
.....
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
<bean class="mypackage.LocalDateObjectMapperContextResolver"/>
</jaxrs:providers>
</jaxrs:server>
Any reason why getContext is not called?
I also tried by extending ObjectMapper and registering javaTimeModule there, but dont know how to register customObjectMapper in Jackson flow. I just put default constructor for testing, And it does get called while application startup, but then again, No results, I still get same error.
Error: No suitable constructor found for type [simple type, class java.time.LocalDate]: can not instantiate from JSON object (need to add/enable type information?)
I had exactly the same problem #peeskillet describes in question comment.
I was using Jackson dependencies from version 2 and jackson-jaxrs from version 1.
All solved when moved all dependencies to version 2.
If you are using Maven you can add following two maven dependency.
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
And Add following code snippet.
#Configuration
public class CxfConfig {
#Component
#javax.ws.rs.ext.Provider
public static class JacksonJaxbJsonProvider
extends com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider {
#Autowired
private ObjectMapper objectMapper;
#PostConstruct
public void init() {
objectMapper.registerModule(new Jdk8Module());
}
}
}

Spring Boot Microservice Jackson JSON serialisation NON NULL

I'm currently working on a Spring Boot ( Version 1.3.1 ) Microservice which connects to MongoDB backend and provides the backend data ( Ex: Provider object ) to the client via controller.
The project has got one class file which extends ResourceSupport ( Ex: ProviderResourceSupport ) and also another class which extends ResourceSupportAssembler class ( Ex: ProviderAssembler ) for generating Links to the Response objects.
Ideally my requirement is to customise the JSON objects on a need basis and as such using #JsonView ( followed this link - https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring ) and added Spring Jackson dependencies in the maven project.
I have also added spring.jackson.serialization-inclusion=non-null & spring.jackson.serialization.indent_output=true in the application.properties.
For one of the method in the controller, the response will be 'ResponseEntity< List< ProviderResourceSupport>>' , and this method is returning with a 'null' response if the data is not present.
I have added #JsonInclude(Include=NON_NULL) on my entity objects and controllers but still getting the 'null' response.
I don't want the 'null' as the response and request you to help me incase if anyone has faced the similar issue.
I fixed this null properties escaping from json response extending a Jackson Mapper Bean but I don't use Spring Boot, take a quickly look and check if this is suitable for you
public class Jackson2ObjectMapperCustom extends Jackson2ObjectMapperFactoryBean {
#Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
getObject().setSerializationInclusion(JsonInclude.Include.NON_NULL).setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
Hibernate5Module hibernateModule = new Hibernate5Module();
hibernateModule.disable(Feature.USE_TRANSIENT_ANNOTATION);
hibernateModule.enable(Feature.FORCE_LAZY_LOADING);
getObject().registerModules(new JavaTimeModule(), hibernateModule);
getObject().configure(SerializationFeature.INDENT_OUTPUT, true);
getObject().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
getObject().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));
}
}
And in my case I use Spring Xml configuration
<bean id="objectMapper" class="com.xxx.common.Jackson2ObjectMapperCustom" />
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="objectMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

Consume a RESTful WS using Spring MVC and Jackson

I would like to consume a RESTful WS using Spring and Jackson.
I'm considering a JSON stream fetched by using Facebook Graph (FC Juventus's JSON data-stream)
This is my controller:
#Controller
public class ConsumeWSController {
#RequestMapping(value = "/consumews", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Consume a RESTful webservice.", locale);
RestTemplate restTemplate = new RestTemplate();
Page page = restTemplate.getForObject("http://graph.facebook.com/juventus", Page.class);
model.addAttribute("pageAbout", page.getAbout());
model.addAttribute("pageAwards", page.getAwards());
return "consumews";
}
}
And the Page class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Page {
private String about;
private String awards;
public String getAbout() {
return about;
}
public void setAbout(String about) {
this.about = about;
}
public String getAwards() {
return awards;
}
public void setAwards(String awards) {
this.awards = awards;
}
}
But the console returns this error:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [my.proj.Page] and content type [application/json;charset=UTF-8]
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet.java:734)
javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
How can I fix this error?
Make sure that you have added the correct Jackson package to your classpath. For Jackson 2 and you use Maven:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.3.1</version>
</dependency>
Or if you use the old Jackson add:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.13</version>
</dependency>
You need to define Jackson as your default message converter for JSON content. This is what I do (I use GSON so this might not be the exact syntax for the Jackson message converter):
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
</list>
</property>
</bean>
But since you're not defining your RestTemplate as a Spring-managed bean, you need to do it manually:
restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
PS. I see you're using the newer Jackson dependency so the proper mapper might be different in that case.

Registrer MappingJackson2HttpMessageConverter in Spring 3.1.2 with JAXB annotations

I have a number of entities with JAXB annotations that I would like to convert to JSON using a message-converter.
I know that my ObjectMapper that reads the JAXB annotations works:
String correctJsonText = jacksonObjectMapper.writeValueAsString(entityWithJAXB);
But when i call my rest service the default registered MappingJacksonHttpMessageConverter (which is not configured for reading JAXB) seems to take over - resulting in a stackoverflow due to cyclic references when #XmlTransient is ignored...
How do i configure Spring to use MappingJackson2HttpMessageConverter ?
Current Configuration
<mvc:annotation-driven>
<mvc:message-converters register-defaults="false">
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
<property name="supportedMediaTypes">
<list>
<bean class="org.springframework.http.MediaType">
<constructor-arg index="0" value="application" />
<constructor-arg index="1" value="json" />
<constructor-arg index="2" value="UTF-8" />
</bean>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="jaxbAnnotationInspector" class="com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector" />
<bean id="jacksonObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="annotationIntrospector" ref="jaxbAnnotationInspector" />
</bean>
REST service
#RequestMapping(value="/{id}", method=RequestMethod.GET, produces = "application/json;charset=UTF-8")
public #ResponseBody EntityWithJAXB readEntityWithJAXB(#PathVariable int id, Model model) {
return entityService.getById(id);
}
Dependencies
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>2.0.5</version>
</dependency>
UPDATE / Solution
By debugging my context I could see that the configuration in <mvc:annotation-driven> had no effect.
It turned out that my unit test with MockMcv always loaded the default handleradapters, thus ignoring my custom objectmapper. Due to convenience I only tested the controller using junit test, since it hit the controller just fine I did not think of this as a probable error cause...
I did not find a fix for my test yet, but when I call the service using curl everything works!
UPDATE / Final solution
Just found a solution for my test setup; when using MockMvc (spring-test-mvc) you must specify custom message-converters explicit:
private MockMvc mockMvc;
#Autowired
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
#Autowired
private RestController restController;
#Before
public void initMockMvc(){
mockMvc = MockMvcBuilders.standaloneSetup(restController)
.setMessageConverters(jacksonMessageConverter).build();
}
#Test
public void testRestController() throws Exception{
DefaultRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/json/42");
this.mockMvc.perform(requestBuilder).andDo(print()).andExpect(status().isOk());
}
The only remaining issue is that the jacksonMessageConverter can't be autowired directly until the following JIRA is resolved: https://jira.springsource.org/browse/SPR-9469. Until then, I have just created a copy of the jacksonMessageConverter in my test context.
I replicated your configuration and it works perfectly for me. The default message converters should not take effect, as you have explicitly specified the register-defaults=false attribute. This is how my #XmlTransient annotation looks:
#XmlAccessorType(XmlAccessType.FIELD)
public class EntityWithJAXB {
#XmlTransient
private EntityWithJAXB aChild;

Jackson serializationConfig

I am using Jackson JSON in a Spring 3 MVC app. To not serialize each and every single Date field, I created a custom objectmapper that uses a specific DateFormat:
#Component("jacksonObjectMapper")
public class CustomObjectMapper extends ObjectMapper
{
Logger log = Logger.getLogger(CustomObjectMapper.class);
#PostConstruct
public void afterProps()
{
log.info("PostConstruct... RUNNING");
//ISO 8601
getSerializationConfig().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SZ"));
}
//constructors...
}
This custom ObjectMapper is injected into the JsonConverter:
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="objectMapper" ref="jacksonObjectMapper" /> <!-- defined in CustomObjectMapper -->
</bean>
There is no exception in the logs and serialization works, but it is not picking up the dateformat, it simple serializes to a timestamp. The #PostConstruct annotation works, the log statement in the method is in the logs.
Does anyone know why this fails?
You may also need to specify that you want textual Date serialization, by doing:
configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
(although I was assuming setting non-null date format might also trigger it, but maybe not)
Also, you can do configuration of mapper directly from constructor (which is safe). Not that it should change behavior, but would remove need for separate configuration method.
I've done the below which works to get around compatability with Java / PHP timestamps. Java uses milliseconds since EPOCH and PHP uses seconds so was simpler to use ISO dates.
I declare the below message adapters:
<bean id="messageAdapter"
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean id="jacksonJsonMessageConvertor"
class="my.app.MyMappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
And MyMappingJacksonHttpMessageConverter looks like the below:
public class MyMappingJacksonHttpMessageConverter extends MappingJacksonHttpMessageConverter {
public MyMappingJacksonHttpMessageConverter(){
super();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(Feature.WRITE_DATES_AS_TIMESTAMPS, false);
setObjectMapper(objectMapper);
}
}
With the above all dates are written out in ISO format.
For Spring config application.properties
spring.jackson.serialization.fail-on-empty-beans=false