Spring mvc rest, json and xml output contain the package name - json

We need to support XML and JSON output from a objects, I have a List which i display in json and xml format, the problem i am facing is it also shows the underlying class, below i am showing all part of codes starting with
output
domain objects
configuration &
controller
**XML Output**
"<**com.bookstore.Books**> Unwanted
<books>
<com.bookstore.Book>
<name>book0</name>
</com.bookstore.Book>
<com.bookstore.Book>
<name>book1</name>
</com.bookstore.Book>
</books>
</com.bookstore.Books>"
**JSON Output** Unwanted part is in bold
**"org.springframework.validation.BindingResult.books**"
:[{"isbn":"03601","authors":[{"autho
I have defind book class as
#XStreamAlias("book")
public class Book {....}
and Books
#XStreamAlias("books")
public class Books {...
List<Book> books;
The context settings are like this
<beans:bean id="xmlView"
class="org.springframework.web.servlet.view.xml.MarshallingView">
<beans:constructor-arg>
<beans:bean class="org.springframework.oxm.xstream.XStreamMarshaller">
<beans:property name="autodetectAnnotations" value="true"/>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<beans:bean id="jsonView"
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
</beans:bean>
<beans:bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
</beans:bean>
The controller part is :
#RequestMapping(value = "/books/xml")
public ModelAndView getAllBooksXML() {
List<Book> books = bookService.getAllBooks();
ModelAndView mav =
new ModelAndView("xmlView", BindingResult.MODEL_KEY_PREFIX + "books", books);
return mav;
}
#RequestMapping(value = "/books/json")
public ModelAndView getAllBooksJson() {
List<Book> books = bookService.getAllBooks();
ModelAndView mav =
new ModelAndView("jsonView", BindingResult.MODEL_KEY_PREFIX + "books", books);
return mav;
}
Please let me know otherwie i will have t
i write a custom converter.

The code you provided seems to use XStream annotations while your spring configuration is configured to use jackson.
Have you tried the annotations supported by jackson?
https://github.com/FasterXML/jackson-annotations/wiki/JacksonAnnotations

Related

JSON reponse with Spring Controllers in Jetty vs Tomcat

I am building a simple Spring MVC webapp and was developing on jetty. My controller binding used this:
#RequestMapping(value = RESTRoutes.CREATE_DOC, method = RequestMethod.POST)
public #ResponseBody String getDoc
And returning a String from a JSONObject correctly resolves to JSON in my ajax response.
But using those same controllers, i deployed my gradle war to tomcat and my json came back wrapped as true strings.
So i changed my headers to use Map and that seems to fix things in both jetty and tomcat:
#RequestMapping(value = RESTRoutes.CREATE_DOC, method = RequestMethod.POST)
public #ResponseBody Map<String, String> getDoc
I convert from the string to a map with this:
HashMap<String, String> jsonResponse = new HashMap<String, String>();
if(claimFolder.has("error")){
response.setStatus(500);
}else{
jsonResponse = new ObjectMapper().readValue(claimFolder.toString(), HashMap.class);
}
return jsonResponse;
My question is why this is nessesary?
Here's my jackson converter configuration:
<bean id="formConverter" class="org.springframework.http.converter.FormHttpMessageConverter" />
<!-- add byte[] converter -->
<bean id="byteArrayConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
<property name="supportedMediaTypes" value="application/octet-stream" />
</bean>
<!-- add in our JSON message converter -->
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json;charset=UTF-8" />
</bean>
<!-- add in our plain string message converter -->
<bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/plain;charset=UTF-8" />
</bean>
<!-- Expose the authenticated handler to all beans that have been declared via annotation -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
</bean>
TL;DR: Why does jetty and tomcat return stringified JSON differently?
Well, it's absolutely normal for Spring content negotiation to translate a String object as a simple string without marshalling it to a JSON object. In order to serialize a java String object in JSON object you need to wrap it previously in some java class. For example:
QuestionStatus {
private String status;
public QuestionStatus(String status) {
this.status = status;
}
public getStatus() {
return status;
}
}
Hence you have to return in your Controller method not a String but QuestionStatus.

spring-mvc return raw json string

I want the convenience of automatically serializing objects into JSON and ability to return raw JSON string. I am using Gson instead of Jackson, since Gson has been in my app for a while and I have existing tweaks, converters, and annotations peppered throughout my app.
<mvc:annotation-driven >
<mvc:message-converters register-defaults="true">
<bean class="com.test.GSONHttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
I can automatically serialize pojo's:
#RequestMapping(value="foo/{name}", method = RequestMethod.GET)
public #ResponseBody Shop getShopInJSON(#PathVariable String name) {
return new Shop();
}
I want this to work also:
#RequestMapping(value="rawJsonTest/{name}", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody String rawJsonTest(#PathVariable String name) {
return "{\"test\":5}";
}
Result right now is an escaped value:
"{\"test\":5}"
instead of:
{"test":5}
The problem is that your custom converter takes precedence over the default ones. It's thus called, considers the String as a raw String that must be converted to JSON, and thus escapes the double quotes.
I'm not sure if and how it's possible with XML to register a converter after (and not before) the default ones, but you could set register-defaults to false and provide an explicit list of all the converters you want to apply. If org.springframework.http.converter.StringHttpMessageConverter is registered before your custom one, it will be called first and will send the returned String as is.
Thanks for the correct answer, #JB Nizet
Order matters:
<mvc:annotation-driven >
<mvc:message-converters register-defaults="true">
<bean class = "org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json; charset=UTF-8" />
</bean>
<bean class="com.test.GSONHttpMessageConverter" />
</mvc:message-converters>

Spring #ResponseBody Jackson JsonSerializer with JodaTime

I have below Serializer for JodaTime handling:
public class JodaDateTimeJsonSerializer extends JsonSerializer<DateTime> {
private static final String dateFormat = ("MM/dd/yyyy");
#Override
public void serialize(DateTime date, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonProcessingException {
String formattedDate = DateTimeFormat.forPattern(dateFormat).print(date);
gen.writeString(formattedDate);
}
}
Then, on each model objects, I do this:
#JsonSerialize(using=JodaDateTimeJsonSerializer.class )
public DateTime getEffectiveDate() {
return effectiveDate;
}
With above settings, #ResponseBody and Jackson Mapper sure works. However, I don't like the idea where I keep writing #JsonSerialize. What I need is a solution without the #JsonSerialize on model objects. Is it possible to write this configuration somewhere in spring xml as a one configuration?
Appreciate your help.
Although you can put an annotation for each date field, is better to do a global configuration for your object mapper. If you use jackson you can configure your spring as follow:
<bean id="jacksonObjectMapper" class="com.company.CustomObjectMapper" />
<bean id="jacksonSerializationConfig" class="org.codehaus.jackson.map.SerializationConfig"
factory-bean="jacksonObjectMapper" factory-method="getSerializationConfig" >
</bean>
For CustomObjectMapper:
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
super();
configure(Feature.WRITE_DATES_AS_TIMESTAMPS, false);
setDateFormat(new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'ZZZ (z)"));
}
}
Of course, SimpleDateFormat can use any format you need.
#Moesio pretty much got it. Here's my config:
<!-- Configures the #Controller programming model -->
<mvc:annotation-driven/>
<!-- Instantiation of the Default serializer in order to configure it -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterConfigurer" init-method="init">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</list>
</property>
</bean>
<bean id="jacksonObjectMapper" class="My Custom ObjectMapper"/>
<bean id="jacksonSerializationConfig" class="org.codehaus.jackson.map.SerializationConfig"
factory-bean="jacksonObjectMapper" factory-method="getSerializationConfig" />
The bit that got me is that <mvc:annotation-driven/> makes its own AnnotationMethodHandler and ignores the one you make manually. I got the BeanPostProcessing idea from http://scottfrederick.blogspot.com/2011/03/customizing-spring-3-mvcannotation.html to configure the one that gets used, and voilĂ ! Works like a charm.
Same using JavaConfig of Spring 3:
#Configuration
#ComponentScan()
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter
{
#Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters)
{
converters.add(0, jsonConverter());
}
#Bean
public MappingJacksonHttpMessageConverter jsonConverter()
{
final MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter();
converter.setObjectMapper(new CustomObjectMapper());
return converter;
}
}
If you are using Spring Boot, try this in application.yml :
spring:
jackson:
date-format: yyyy-MM-dd
time-zone: Asia/Shanghai
joda-date-time-format: yyyy-MM-dd
If you simply have the Jackson JARs on your classpath, and return a #ResponseBody, Spring will automatically convert the Model object to JSON. You don't need to annotate anything in the Model to get this to work.

When using Spring MVC for REST, how do you enable Jackson to pretty-print rendered JSON?

While developing REST services using Spring MVC, I would like render JSON 'pretty printed' in development but normal (reduced whitespace) in production.
If you are using Spring Boot 1.2 or later the simple solution is to add
spring.jackson.serialization.INDENT_OUTPUT=true
to the application.properties file. This assumes that you are using Jackson for serialization.
If you are using an earlier version of Spring Boot then you can add
http.mappers.json-pretty-print=true
This solution still works with Spring Boot 1.2 but it is deprecated and will eventually be removed entirely. You will get a deprecation warning in the log at startup time.
(tested using spring-boot-starter-web)
I had an answer when I posted this question, but I thought I'd post it anyway in case there are better alternative solutions. Here was my experience:
First thing's first. The MappingJacksonHttpMessageConverter expects you to inject a Jackson ObjectMapper instance and perform Jackson configuration on that instance (and not through a Spring class).
I thought it would be as easy as doing this:
Create an ObjectMapperFactoryBean implementation that allows me to customize the ObjectMapper instance that can be injected into the MappingJacksonHttpMessageConverter. For example:
<bean id="jacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper">
<bean class="com.foo.my.ObjectMapperFactoryBean">
<property name="prettyPrint" value="${json.prettyPrint}"/>
</bean>
</property>
</bean>
And then, in my ObjectMapperFactoryBean implementation, I could do this (as has been documented as a solution elsewhere on SO):
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, isPrettyPrint());
return mapper;
But it didn't work. And trying to figure out why is a nightmare. It is a major test of patience to figure Jackson out. Looking at its source code only confuses you further as it uses outdated and obtuse forms of configuration (integer bitmasks for turning on/off features? Are you kidding me?)
I essentially had to re-write Spring's MappingJacksonHttpMessageConverter from scratch, and override its writeInternal implementation to be the following:
#Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = getEncoding(outputMessage.getHeaders().getContentType());
JsonGenerator jsonGenerator =
getObjectMapper().getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
try {
if (this.prefixJson) {
jsonGenerator.writeRaw("{} && ");
}
if (isPrettyPrint()) {
jsonGenerator.useDefaultPrettyPrinter();
}
getObjectMapper().writeValue(jsonGenerator, o);
}
catch (JsonGenerationException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
The only thing I added to the existing implementation is the following block:
if (isPrettyPrint()) {
jsonGenerator.useDefaultPrettyPrinter();
}
isPrettyPrint() is just a JavaBeans compatible getter w/ matching setter that I added to my MappingJacksonHttpMessageConverter subclass.
Only after jumping through these hoops was I able to turn on or off pretty printing based on my ${json.prettyPrint} value (that is set as a property depending on how the app is deployed).
I hope this helps someone out in the future!
When you are using Jackson 2.0.0, you can do it in a way Les wanted to.
I currently use RC3 and the configuration seems to be working as expected.
ObjectMapper jacksonMapper = new ObjectMapper();
jacksonMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
translates
{"foo":"foo","bar":{"field1":"field1","field2":"field2"}}
into
{
"foo" : "foo",
"bar" : {
"field1" : "field1",
"field2" : "field2"
}
}
Might I suggest this approach, it is valid with Spring 4.0.x and possibly older versions.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper());
return mappingJackson2HttpMessageConverter;
}
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objMapper = new ObjectMapper();
objMapper.enable(SerializationFeature.INDENT_OUTPUT);
return objMapper;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(mappingJackson2HttpMessageConverter());
}
}
Thanks to Willie Wheeler for the solution: Willie Wheeler's Spring blog
How do I make Jackson pretty-print the JSON content it generates?
Here's a simple example:
Original JSON Input:
{"one":"AAA","two":["BBB","CCC"],"three":{"four":"DDD","five":["EEE","FFF"]}}
Foo.java:
import java.io.FileReader;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
public class Foo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
MyClass myObject = mapper.readValue(new FileReader("input.json"), MyClass.class);
// this is Jackson 1.x API only:
ObjectWriter writer = mapper.defaultPrettyPrintingWriter();
// ***IMPORTANT!!!*** for Jackson 2.x use the line below instead of the one above:
// ObjectWriter writer = mapper.writer().withDefaultPrettyPrinter();
System.out.println(writer.writeValueAsString(myObject));
}
}
class MyClass
{
String one;
String[] two;
MyOtherClass three;
public String getOne() {return one;}
void setOne(String one) {this.one = one;}
public String[] getTwo() {return two;}
void setTwo(String[] two) {this.two = two;}
public MyOtherClass getThree() {return three;}
void setThree(MyOtherClass three) {this.three = three;}
}
class MyOtherClass
{
String four;
String[] five;
public String getFour() {return four;}
void setFour(String four) {this.four = four;}
public String[] getFive() {return five;}
void setFive(String[] five) {this.five = five;}
}
Output:
{
"one" : "AAA",
"two" : [ "BBB", "CCC" ],
"three" : {
"four" : "DDD",
"five" : [ "EEE", "FFF" ]
}
}
If this approach doesn't exactly fit your needs, if you search the API docs v1.8.1 for "pretty", it'll turn up the relevant components available. If you use API version 2.x then look instead at the newer API 2.1.0 docs.
Pretty print will be enable by adding and configure the MappingJackson2HttpMessageConverter converter. Disable prettyprint within production environment.
Message converter configuration
<mvc:annotation-driven>
<mvc:message-converters>
<bean id="jacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="prettyPrint" value="${json.prettyPrint}" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Based on baeldung this could be a nice idea using java 8:
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
Optional<HttpMessageConverter<?>> converterFound;
converterFound = converters.stream().filter(c -> c instanceof AbstractJackson2HttpMessageConverter).findFirst();
if (converterFound.isPresent()) {
final AbstractJackson2HttpMessageConverter converter;
converter = (AbstractJackson2HttpMessageConverter) converterFound.get();
converter.getObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
converter.getObjectMapper().enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
}
I had trouble getting the custom MappingJacksonHttpMessageConverter to work as suggested above but I was finally able to get it to work after struggling w/ the configuration. From the code stand point I did exactly what was mentioned above but I had to add the following configuration to my springapp-servlet.xml to get it to work.
I hope this helps others who are looking to implement the same.
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
</list>
</property>
</bean>
<bean id="jsonConverter" class="com.xxx.xxx.xxx.common.PrettyPrintMappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="prettyPrint" value="true" />
</bean>
Jackson 2 has a nicer API, agreed, but it won't resolve this problem in a Spring MVC environment given Spring MVC uses ObjectMapper#writeValue(JsonGenerator, Object) to write objects out as JSON. This writeValue variant does not apply ObjectMapper serialization features such as INDENT_OUTPUT in either Jackson 1.x or 2.0.
I do think this is somewhat confusing. Since we use the ObjectMapper to construct JsonGenerators, I'd expect returned generators to be initialized based on configured ObjectMapper settings. I reported this as a issue against Jackson 2.0 here: https://github.com/FasterXML/jackson-databind/issues/12.
Les's suggestion of calling JsonGenerator#useDefaultPrettyPrinter based on the value of a prettyPrint flag is about the best we can do at the moment. I've gone ahead and created a Jackson2 HttpMessageConverter that does this based on the enabled status of the INDENT_OUTPUT SerializationFeature: https://gist.github.com/2423129.
I would make that a rendering issue, not the concern of the REST service.
Who's doing the rendering? Let that component format the JSON. Maybe it can be two URLs - one for production and another for development.

Spring3 REST Web Services with Jackson JSONViews

I got a plain spring3 web project set up and have a controller method like this:
#RequestMapping(method = RequestMethod.GET, value = "/book/{id}", headers = "Accept=application/json,application/xml")
public #ResponseBody
Book getBook(#PathVariable final String id)
{
logger.warn("id=" + id);
return new Book("12345", new Date(), "Sven Haiges");
}
It returns a new book object which will be transformed to JSON or XML because of the transformers I setup in the spring config:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
<ref bean="marshallingConverter" />
</list>
</property>
</bean>
JSON generation (and XML) all works, but I would like to be able to define multiple views for the data. For example I'd like to specify a detailed view with less properties in the exposed JSON/XML and a detailed view with the full set of properties.
Using Jackson's ObjectMapper this is possible like this:
objectMapper.writeValueUsingView(out, beanInstance, ViewsPublic.class);
Is there a way I can configure Spring to use a specific VIEW (detailed/summary)? The only way to achieve this right now is to use different DTOs returned from my controller methods.
Thanx!
If you need that level of control, then you need to do it yourself.
So rather than using #ResponseBody, instead use your own ObjectMapper to write the response manually, e.g.
private final ObjectMapper objectMapper = new ObjectMapper();
#RequestMapping(method = RequestMethod.GET, value = "/book/{id}", headers = "Accept=application/json,application/xml")
public void getBook(#PathVariable final String id, HttpServletResponse httpResponse)
{
logger.warn("id=" + id);
Book book = new Book("12345", new Date(), "Sven Haiges");
objectMapper.writeValueUsingView(httpResponse.getWriter(), book, ViewsPublic.class);
}
By the way, writeValueUsingView is deprecated in the current version of JSON (see javadoc).