Could you please help me to configure correctly Jackson mix-in annotation with Spring MVC to customize a JSON response.
This is what I have now :
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
I don't know how to configure it to map the 2 classes ... : addMixInAnnotations(User.class, UserMixIn.class);
dd the mixin configuration to the ObjectMapper once initialized in your Controller constructor:
#Controller
public class MyController {
private ObjectMapper objectMapper = new ObjectMapper();
public MyController(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
objectMapper.addMixInAnnotations(User.class, UserMixIn.class);
}
#RequestMapping("/some-path")
#ResponseBody
public String someMethod() {
List<User> users = new ArrayList<User>(); // Mock List to hold your Users
users.add(new User()); // Keep adding some users
return objectMapper.writeValueAsString(users, new TypeReference<List<User>>() {});
}
}
And check out the output :)
The solution given by #tmarwen will work just fine if you only need to configure the Mixin for a single controller.
However if you want to use the mixin thoughout all Spring controllers you need implement an approach similar to the following:
Change your XML configuration to:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="mapper"/>
</bean>
Next you need to configure the mapper bean that is referenced in the XML above. You could easily do that in XML with the use of SpEL and FactoryBean, but there is no good reason to do so when you have a great and super easy to use alternative in Java Config.
#Configuration
public class JacksonConfig {
#Bean
public ObjectMapper mapper() {
final ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(User.class, UserMixIn.class);
return mapper;
}
}
With the changes above in place, you need absolutely no reference to ObjectMapper in your controllers and can use Spring MVC's JSON features just like you are using them now.
Related
I have a rest endpoint(A) that returns a large json object. I have to return it to another rest api(B) in a different format. Would it be beneficial to map it to an object, transform the data into the object the other REST API(B) is expecting?
Would it be better to use a json library and pull out the parts I need and transform the data that way?
Thanks
I use Spring for my web apps. in my context config, I have:
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jacksonMessageConverter"/>
</list>
</property>
</bean>
Then in my controller code, I use:
#RequestMapping(...)
public #ResponseBody MyPojo getMyPojo(#PathVariable("id") Integer id, #RequestBody MyOtherPojo) {
MyPojo myPojo = new MyPojo();
// do some business methods
return myPojo
}
The #ResponseBody MyPojo tells the jacksonMessageConverter to convert the MyPojo to a JSON object, and the #RequestBody tells the jacksonMessageConverter to convert a JSON variable into an instance of MyOtherPojo.
I have a JAX-RS API using Apache CXF. Recently I switched from Jettison to Jackson (2.7.1) for JSON handling. I am using JacksonJaxbJsonProvider.
One thing I need to do to help with transition is to ensure that null fields in JSON are not being rendered. For that I am using following annotation on class level:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Book {
...
}
Is there any way to set it globally, so that I do not have to apply this annotation to every class?
With Jettison I was able to specify properties in beans.xml file, is there a similar approach possible with Jackson to achieve NON_NULL behavior?
<bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
<property name="dropRootElement" value="true"/>
<property name="supportUnwrapped" value="true"/>
</bean>
I have seen some suggestion to set up this property on ObjectMapper level when instantiated, however when using Apache CXF I never create ObjectMapper by hand, as this instantiation is handled by the framework (probably happens somewhere in JacksonJaxbJsonProvider).
Is there any way to set NON_NULL property globally?
Just in case it helps somebody else, configuring ObjectMapper in beans.xml worked with NON_NULL however my JAXB annotations stopped working. To get both of them working at the same time I resorted to creating my own ObjectMapper provider:
#Provider
public class CustomJacksonObjectMapperProvider implements ContextResolver<ObjectMapper> {
final ObjectMapper defaultObjectMapper;
public CustomJacksonObjectMapperProvider() {
defaultObjectMapper = createDefaultMapper();
}
#Override
public ObjectMapper getContext(Class<?> type) {
return (defaultObjectMapper);
}
private static ObjectMapper createDefaultMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setAnnotationIntrospector(
AnnotationIntrospector.pair(
new JacksonAnnotationIntrospector(),
new JaxbAnnotationIntrospector(mapper.getTypeFactory())
));
return (mapper);
}
}
and registering it in beans.xml (under jaxrs:providers) as follows:
<bean id="customJacksonObjectMapperProvider" class="CustomJacksonObjectMapperProvider"/>
You can include the following configuration as below:
<bean id="jacksonJaxbJsonProvider"
class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider">
<constructor-arg ref="objectMapper"></constructor-arg>
</bean>
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="serializationInclusion" value="NON_NULL"></property>
</bean>
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I have case insensitive URLS in Spring MVC with annotated mappings
I have Controller having multiple #RequestMapping annotations in it.
#Controller
public class SignUpController {
#RequestMapping("signup")
public String showSignUp() throws Exception {
return "somejsp";
}
#RequestMapping("fullSignup")
public String showFullSignUp() throws Exception {
return "anotherjsp";
}
#RequestMapping("signup/createAccount")
public String createAccount() throws Exception {
return "anyjsp";
}
}
How can I map these #RequestMapping to case insensitive. i.e. if I use "/fullsignup" or "/fullSignup" I should get "anotherjsp". But this is not happening right now. Only "/fullSignup" is working fine.
I've tried extending RequestMappingHandlerMapping but no success. I've also tried AntPathMatcher like the guy mentioned there is another question on this forum but its also not working for #RequestMapping annotation.
Debugging console
Output console when server is up.
I've added two images which shows the problem. I've tried both the solutions mentioned below. The console says that it mapped lowercased URLS but when I request to access a method with lowercase url then it shows that the original map where the values are stored stilled contained MixCase URLS.
One of the approaches in How can I have case insensitive URLS in Spring MVC with annotated mappings works perfectly. I just tried it with combinations of #RequestMapping at the level of controller and request methods and it has worked cleanly, I am just reproducing it here for Spring 3.1.2:
The CaseInsensitivePathMatcher:
import java.util.Map;
import org.springframework.util.AntPathMatcher;
public class CaseInsensitivePathMatcher extends AntPathMatcher {
#Override
protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {
return super.doMatch(pattern.toLowerCase(), path.toLowerCase(), fullMatch, uriTemplateVariables);
}
}
Registering this path matcher with Spring MVC, remove the <mvc:annotation-driven/> annotation, and replace with the following, configure appropriately:
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService"></property>
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
</bean>
</property>
</bean>
</property>
<property name="messageConverters">
<list>
<ref bean="byteArrayConverter"/>
<ref bean="jaxbConverter"/>
<ref bean="jsonConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
</list>
</property>
</bean>
<bean name="byteArrayConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean name="jaxbConverter" class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
<bean name="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
<bean name="caseInsensitivePathMatcher" class="org.bk.lmt.web.spring.CaseInsensitivePathMatcher"/>
<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="pathMatcher" ref="caseInsensitivePathMatcher"></property>
</bean>
Or even more easily and cleanly using #Configuration:
#Configuration
#ComponentScan(basePackages="org.bk.webtestuuid")
public class WebConfiguration extends WebMvcConfigurationSupport{
#Bean
public PathMatcher pathMatcher(){
return new CaseInsensitivePathMatcher();
}
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setPathMatcher(pathMatcher());
return handlerMapping;
}
}
The following simple solution should make #RequestMapping insensitive, whether it annotates a Controller or a method. Biju's solution should work too.
Create this custom HandlerMapping :
public CaseInsensitiveAnnotationHandlerMapping extends DefaultAnnotationHandlerMapping {
#Override
protected Object lookupHandler(String urlPath, HttpServletRequest request)
throws Exception {
return super.lookupHandler(urlPath.toLowerCase(), request);
}
#Override
protected void registerHandler(String urlPath, Object handler)
throws BeansException, IllegalStateException {
super.registerHandler(urlPath.toLowerCase(), handler);
}
}
And add this in your [servlet-name]-servlet.xml :
<bean class="yourpackage.CaseInsensitiveAnnotationHandlerMapping" />
Note: if you don't want two HandlerMapping in your app, you may want to remove <mvc:annotation-driven /> (it instantiates a DefaultAnnotationHandlerMapping).
I was wondering if it is possible to specify x amount of the same bean in a list in Spring. For example, instead of having beans with ids: stage1, stage2,... stageN, as here:
<bean id="stage1" class="Stageclass"/>
<bean id="stage2" class="Stageclass"/>
<bean id="stages" class="java.util.ArrayList">
<constructor-arg>
<list>
<ref bean="stage1" />
<ref bean="stage2" />
</list>
</constructor-arg>
</bean>
Would it be possible to do something like the following?:
<bean id="stage1" class="Stageclass"/>
<bean id="stages" class="java.util.ArrayList">
<constructor-arg>
<list>
<ref bean="stage1" duplicate="20 times"/>
</list>
</constructor-arg>
</bean>
Thanks in advance.
If you use annotation based configuration and you specified list of objects with same interface as dependency for some class then spring will auto-wire aut-wire then for free. Example:
interface StageInterface {
//...
}
class StageImpl1 implements StageInterface {
//...
}
class StageImpl2 implements StageInterface {
//...
}
#Component
class StageContainer {
private final List<StageInterface> stages;
#Autowired
public StageContainer(List<StageInterface> stages) {
this.stages = stages;
}
public List<StageInterface> getStages() {
return stages;
}
}
This is a spring version 3+ feature.
I believe the same is possible with xml configuration as well. In your case that's probably will be the same class(StageClass), but with different configuration parameters.
Lookup method injection from http://static.springsource.org/spring/docs/2.5.x/reference/beans.html solved the problem. Just needed to make sure the bean I wanted multiple instances of had scope="prototype"
You can't do that using standard Spring's default namespace. However you can implement your own custom namespace where you could support such syntax.
Alternatively, you can implement a static method that would create an ArrayList instance with duplicated elements.
I've implemented a RESTful web service with Spring. The service responds in XML or JSON based on the Accept header. Here's the context.xml mapping:
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"/>
<bean id="xmlMessageConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<constructor-arg ref="xstreamMarshaller"/>
<property name="supportedMediaTypes" value="application/xml"/>
</bean>
<bean id="jsonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="prefixJson" value="false"/>
<property name="supportedMediaTypes" value="application/json"/>
</bean>
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list id="beanList">
<ref bean="xmlMessageConverter"/>
<ref bean="jsonHttpMessageConverter"/>
</util:list>
</property>
</bean>
Here is my controller method:
#Controller
#RequestMapping(value = "/entityService")
class RestfulEntityService {
#Resource
private EntityService entityService;
#ResponseBody
#RequestMapping(value = "/getAllEntities", method = RequestMethod.GET)
public List<Entity> getAllEntities() {
return entityService.getAllEntities();
}
}
The XML response is valid, however, when the client sets the Accept header to application/json, the response is invalid JSON.
Here is the JSON response sample:
[{"id":3,"attributes":[{"id":18,"attributeValue":null,"attributeName":"mobile","attributeType":"varchar(40)","entity":{"id":3,"attributes":[{"id":18,"attributeValue":null,"attributeName":"mobile","attributeType":"varchar(40)","entity":{"id":3,"attributes":[{"id":18,"attributeValue":null,"attributeName":"mobile","attributeType":"varchar(40)","entity":{"id":3,"attributes": ..... repeats for a while and then stops..
You're using XStream to serialize XML responses and Jackson JSON to serialize JSON responses. Looking at the JSON output you posted, it seems like there's a circular reference issue at hand. I'm guessing Entity has a list of attributes, each pointing to their respective entity. XStream handles circular references transparently by using XPath, this allows to preserve references when deserializing back to objects. Jackson is able to handle circular references since v1.6, but you need to help it by annotating your serialized entities with #JsonManagedReference and #JsonBackReference. I think Jackson is unique in allowing back references in JSON serialization.
See Jackson's documentation on handling bi-directional references using declarative methods for reference.