Example JSON (note that the string has trailing spaces):
{ "aNumber": 0, "aString": "string " }
Ideally, the deserialised instance would have an aString property with a value of "string" (i.e. without trailing spaces). This seems like something that is probably supported but I can't find it (e.g. in DeserializationConfig.Feature).
We're using Spring MVC 3.x so a Spring-based solution would also be fine.
I tried configuring Spring's WebDataBinder based on a suggestion in a forum post but it does not seem to work when using a Jackson message converter:
#InitBinder
public void initBinder( WebDataBinder binder )
{
binder.registerCustomEditor( String.class, new StringTrimmerEditor( " \t\r\n\f", true ) );
}
Easy solution for Spring Boot users, just add that walv's SimpleModule extension to your application context:
package com.example;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.stereotype.Component;
import java.io.IOException;
#Component
public class StringTrimModule extends SimpleModule {
public StringTrimModule() {
addDeserializer(String.class, new StdScalarDeserializer<String>(String.class) {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException,
JsonProcessingException {
return jsonParser.getValueAsString().trim();
}
});
}
}
Another way to customize Jackson is to add beans of type com.fasterxml.jackson.databind.Module to your context. They will be registered with every bean of type ObjectMapper, providing a global mechanism for contributing custom modules when you add new features to your application.
http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper
if you are not using spring boot, you have to register the StringTrimModule yourself (you do not need to annotate it with #Component)
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="modulesToInstall" value="com.example.StringTrimModule"/>
</bean
With a custom deserializer, you could do the following:
<your bean>
#JsonDeserialize(using=WhiteSpaceRemovalSerializer.class)
public void setAString(String aString) {
// body
}
<somewhere>
public class WhiteSpaceRemovalDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jp, DeserializationContext ctxt) {
// This is where you can deserialize your value the way you want.
// Don't know if the following expression is correct, this is just an idea.
return jp.getCurrentToken().asText().trim();
}
}
This solution does imply that this bean attribute will always be serialized this way, and you will have to annotate every attribute that you want to be deserialized this way.
I think it is better to extend default StringDeserializer as it already handles some specific cases (see here and here) that can be used by third party libraries. Below you can find configuration for Spring Boot. This is possible only with Jackson 2.9.0 and above as starting from 2.9.0 version StringDeserializer is not final anymore. If you have Jackson version below 2.9.0 you can still copy content of StringDeserializer to your code to handle above mentioned cases.
#JsonComponent
public class StringDeserializer extends com.fasterxml.jackson.databind.deser.std.StringDeserializer {
#Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = super.deserialize(p, ctxt);
return value != null ? value.trim() : null;
}
}
The problem of annotation #JsonDeserialize is that you must always remember to put it on the setter.
To make it globally "once and forever" with Spring MVC, I did next steps:
pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.3</version>
</dependency>
Create custom ObjectMapper:
package com.mycompany;
import java.io.IOException;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class MyObjectMapper extends ObjectMapper {
public MyObjectMapper() {
registerModule(new MyModule());
}
}
class MyModule extends SimpleModule {
public MyModule() {
addDeserializer(String.class, new StdScalarDeserializer<String> (String.class) {
#Override
public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
return StringUtils.trim(jp.getValueAsString());
}
});
}
}
Update Spring's servlet-context.xml:
<bean id="objectMapper" class="com.mycompany.MyObjectMapper" />
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="objectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
For Spring Boot, we just have to create a custom deserializer as documented in the manual.
The following is my Groovy code but feel free to adapt it to work in Java.
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import org.springframework.boot.jackson.JsonComponent
import static com.fasterxml.jackson.core.JsonToken.VALUE_STRING
#JsonComponent
class TrimmingJsonDeserializer extends JsonDeserializer<String> {
#Override
String deserialize(JsonParser parser, DeserializationContext context) {
parser.hasToken(VALUE_STRING) ? parser.text?.trim() : null
}
}
com.fasterxml.jackson.dataformat
pom.xml
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
<version>2.5.3</version>
</dependency>
CsvUtil.java
CsvSchema bootstrapSchema = CsvSchema.emptySchema().withHeader().sortedBy();
CsvMapper mapper = new CsvMapper();
mapper.enable(CsvParser.Feature.TRIM_SPACES);
InputStream inputStream = ResourceUtils.getURL(fileName).openStream();
MappingIterator<T> readValues =
mapper.readerFor(type).with(bootstrapSchema).readValues(inputStream);
I propose you the following:
First, create a module to trim and put it into a class:
import java.io.IOException;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
#Component
public class StringTrimModule extends SimpleModule {
public StringTrimModule() {
addDeserializer(String.class, new StdScalarDeserializer<String>(String.class) {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
return jsonParser.getValueAsString().trim();
}
});
}
}
Then, create a class to configure jackson and add the module:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Class used to configure Jackson
*/
#Configuration
public class JacksonConfiguration {
#Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new StringTrimModule());
return mapper;
}
}
That's it.
Related
I want to use a JSON processor like Genson in my spring boot application.
When you use Jackson or Gson you are so comfortable due to the spring boot auto-configuration for these two libraries.
In this link, there is a great guide for replacing Jackson with Gson.
For solving my problem, I created the genson and gensonBuilder bean But what should I do to be able to add this line to my config? spring.http.converters.preferred-json-mapper=genson
Do I need to do anything other than the above for replacing Jackson with Genson library?
There is a guide for Spring Boot:
1. First of all exclude JACKSON from your Spring Boot application
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- Exclude the default Jackson dependency -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>
2. Register converter and Genson class
Genson has HTTP converter for Spring com.owlike.genson.ext.spring.GensonMessageConverter, Sources.
In case Spring Boot application just create new bean with this converter. It will be applied automatically.
See Registering a custom MessageConverter in Spring Boot
Documentation
Any HttpMessageConverter bean that is present in the context will be
added to the list of converters
#Bean
public Genson genson() {
return new GensonBuilder().setHtmlSafe(true).setSkipNull(true).useBeanViews(true).create();
}
#Bean
public com.owlike.genson.ext.spring.GensonMessageConverter gensonMessageConverter() {
return new com.owlike.genson.ext.spring.GensonMessageConverter(genson());
}
spring.http.converters.preferred-json-mapper=genson is not required, just remove this config.
To use custom, not supported by default by Spring conversion library you need to implement your own AbstractJsonHttpMessageConverter:
import com.owlike.genson.Genson;
import com.owlike.genson.GensonBuilder;
import org.springframework.http.converter.json.AbstractJsonHttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
public class GensonHttpMessageConverter extends AbstractJsonHttpMessageConverter {
private Genson genson;
public GensonHttpMessageConverter() {
this(new GensonBuilder()
.useIndentation(true)
.create());
}
public GensonHttpMessageConverter(Genson genson) {
Assert.notNull(genson, "A Genson instance is required");
this.genson = genson;
}
public void setGenson(Genson genson) {
Assert.notNull(genson, "A Genson instance is required");
this.genson = genson;
}
public Genson getGenson() {
return this.genson;
}
#Override
protected Object readInternal(Type resolvedType, Reader reader) {
return getGenson().deserializeInto(reader, resolvedType);
}
#Override
protected void writeInternal(Object object, #Nullable Type type, Writer writer) {
getGenson().serialize(object, writer);
}
}
After that you need to register it:
import com.example.genson.GensonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new GensonHttpMessageConverter());
}
}
See also:
Http Message Converters with the Spring Framework
I am trying to localize almost every parameter in the response of each API in my project.
I have figured out that we can do something like this in spring boot:
MessageSourceAccessor accessor = new MessageSourceAccessor(messageSource, locale);
return accessor.getMessage(code);
and keep the code versus localized message mapping in messages_en.properties, messages_fr.properties etc.
But for my application I specifically have two requirements:
I want to separate this logic from my business logic i.e., I don't want to write localization logic in each and every controller.
I want to try it at each and every response parameter for all the response through the server, maybe while Jackson is converting objects to string or after conversion to JSON.
Is there a way in spring boot to achieve this or are there any libraries available for this?
I have found a solution for this. Instead of using String for fields, I am using a custom class like LocalizedText:
import lombok.AllArgsConstructor;
import lombok.Data;
#Data
#AllArgsConstructor
public class LocalizedText {
private String text;
}
For serialization, I have created a Deserializer LocalizedTextSerailizer, something like this:
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
#Component
public class LocalizedTextSerializer extends StdSerializer<LocalizedText> {
private static final long serialVersionUID = 619043384446863988L;
#Autowired
I18nUtil messages;
public LocalizedTextSerializer() {
super(LocalizedText.class);
}
public LocalizedTextSerializer(Class<LocalizedText> t) {
super(t);
}
#Override
public void serialize(LocalizedText value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(messages.get(value.getText()));
}
}
I18nUtil:
import java.util.Locale;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
#Component
#Slf4j
public class I18nUtil {
#Autowired
private MessageSource messageSource;
public String get(String code) {
try {
MessageSourceAccessor accessor = new MessageSourceAccessor(messageSource, Locale.getDefault());
return accessor.getMessage(code);
} catch (NoSuchMessageException nsme) {
log.info("Message not found in localization: " + code);
return code;
}
}
}
This pretty much serves the purpose, I don't have to mess up with the business logic and I can localize any parameter for any response in the application.
Note:
Here I18nUtil, returns the same code if it couldn't find any message in the message.properties.
Default locale is used in I18nUtil, for demonstration.
There are many questions concerning conversion from ObjectId to String with jackson. All answers suggest either creating own JsonSerializer<ObjectId> or annotating the ObjectId field with #JsonSerialize(using = ToStringSerializer.class).
However, I have a map that sometimes contains ObjectIds, i.e.:
class Whatever {
private Map<String, Object> parameters = new HashMap<>();
Whatever() {
parameters.put("tom", "Cat");
parameters.put("jerry", new ObjectId());
}
}
I want jackson to convert it to:
{
"parameters": {
"tom": "cat",
"jerry": "57076a6ed1c5d61930a238c5"
}
}
But I get:
{
"parameters": {
"tom": "cat",
"jerry": {
"date": 1460103790000,
"machineIdentifier": 13747670,
"processIdentifier": 6448,
"counter": 10631365,
"time": 1460103790000,
"timestamp": 1460103790,
"timeSecond": 1460103790
}
}
}
I have registered the conversion (in Spring) with
public class WebappConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder
.serializerByType(ObjectId.class, new ToStringSerializer());
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build());
converters.add(converter);
}
}
And the first-level ObjectIds are converted correctly. How to make jackson convert also the nested ones? Do I have to write custom converter for this map?
Keep in mind that this Map can be nested multiple times (i.e. contain another maps). I just want to convert ObjectId to String whenever jackson sees it.
I suppose that you are taking about org.bson.types.ObjectId from org.springframework.boot:spring-boot-starter-data-mongodb. Your code works perfectly fine for me. 1 thing i can see is that you don't show #Configuration annotation above WebappConfig.
Here is my demo project, can you test it on yours setup?
Application.java
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.bson.types.ObjectId;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Configuration
public static class WebappConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder
.serializerByType(ObjectId.class, new ToStringSerializer());
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build());
converters.add(converter);
}
}
#RestController
public static class MyRestController {
#ResponseBody
#RequestMapping("/")
public Whatever method() {
return new Whatever();
}
}
public static class Whatever {
private Map<String, Object> parameters = new HashMap<>();
public Whatever() {
parameters.put("tom", "Cat");
parameters.put("jerry", new ObjectId());
}
public Map<String, Object> getParameters() {
return parameters;
}
public void setParameters(Map<String, Object> parameters) {
this.parameters = parameters;
}
}
}
Responce from 127.0.0.1:8080
{
"parameters": {
"tom": "Cat",
"jerry": "5709df1cf0d9550b4de619d2"
}
}
Gradle:
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-mongodb")
compile("org.springframework.boot:spring-boot-starter-web")
}
thanks varren's answer, it works fine in springMvc's older version.
but since 5.0, WebMvcConfigurerAdapter was deprecated.
solution:
may not work solution: we can implements WebMvcConfigurer directly for mvc config. but some config may not work, because WebMvcConfigurationSupport's priority is higher.
suggest solution: we can extends WebMvcConfigurationSupport directly. imply configureMessageConverters method can add all kinds of custom HttpMessageConverters we need, and it can works fine before default converters.
spring framework is a amazing framework, I need to look it deeper after I got time.(●'◡'●)
I want to intercept the JSON sent back from a Spring MVC Rest Controller and run it through a sanitizer that ensures it's valid and HTML escapes any dodgy characters. (Possibly the OWASP JSON Sanitizer)
We use the Jackson HTTP Message converter to convert the #ResponseBody to JSON, as far as I can see once I return the object as a #ResponseBody I lose control of it.
Is there a sensible way to intercept the JSON as a String to run sanitization code on it?
I'm currently investigating three avenues:
Writing a Filter and ResponseWrapper which sanitizes the JSON before it's sent back to the client.
Extending the JSON Mapper somehow to provide sanitized JSON.
Writing a Handler Interceptor and using it to modify the response.
I'm not sure if either of these will work or if there is a more sensible third option.
I know this answer may be too late, but I needed to do the same thing, so I added a serializer to the JSON mapper.
The web configuration:
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.fasterxml.jackson.databind.ObjectMapper;
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
// the list is empty, so we just add our converter
converters.add(jsonConverter());
}
#Bean
public HttpMessageConverter<Object> jsonConverter() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder
.json()
.serializerByType(String.class, new SanitizedStringSerializer())
.build();
return new MappingJackson2HttpMessageConverter(objectMapper);
}
}
And the string serializer:
import java.io.IOException;
import org.apache.commons.lang3.StringEscapeUtils;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.NonTypedScalarSerializerBase;
public class SanitizedStringSerializer extends NonTypedScalarSerializerBase<String> {
public SanitizedStringSerializer() {
super(String.class);
}
#Override
public void serialize(String value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException {
jgen.writeRawValue("\"" + StringEscapeUtils.escapeHtml4(value) + "\"");
}
}
in Java when i use the
#Produces("application/json")
annotation the output is not formated into human readable form. How do i achive that?
Just for the record, if you want to enable the pretty output only for some resources you can use the #JacksonFeatures annotation on a resource method.
Here is example:
#Produces(MediaType.APPLICATION_JSON)
#JacksonFeatures(serializationEnable = { SerializationFeature.INDENT_OUTPUT })
public Bean resource() {
return new Bean();
}
This is how you can properly do conditional pretty/non-pretty json output based on presence of "pretty" in query string.
Create a PrettyFilter that implements ContainerResponseFilter, that will be executed on every request:
#Provider
public class PrettyFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext reqCtx, ContainerResponseContext respCtx) throws IOException {
UriInfo uriInfo = reqCtx.getUriInfo();
//log.info("prettyFilter: "+uriInfo.getPath());
MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters();
if(queryParameters.containsKey("pretty")) {
ObjectWriterInjector.set(new IndentingModifier(true));
}
}
public static class IndentingModifier extends ObjectWriterModifier {
private final boolean indent;
public IndentingModifier(boolean indent) {
this.indent = indent;
}
#Override
public ObjectWriter modify(EndpointConfigBase<?> endpointConfigBase, MultivaluedMap<String, Object> multivaluedMap, Object o, ObjectWriter objectWriter, JsonGenerator jsonGenerator) throws IOException {
if(indent) jsonGenerator.useDefaultPrettyPrinter();
return objectWriter;
}
}
}
And pretty much that's it!
You will need to ensure that this class gets used by Jersey by either automated package scanning or registered manually.
Spent few hours trying to achieve that and found that no-one has published a ready-to-use solution before.
Create this class anywhere in your project. It will be loaded on deployment. Notice the .configure(SerializationConfig.Feature.INDENT_OUTPUT, true); which configures the mapper to format the output.
For Jackson 2.0 and later, replace the two .configure() lines with these:
.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
.configure(SerializationFeature.INDENT_OUTPUT, true);
And change your imports accordingly.
package com.secret;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
/**
*
* #author secret
*/
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JacksonContextResolver implements ContextResolver<ObjectMapper> {
private ObjectMapper objectMapper;
public JacksonContextResolver() throws Exception {
this.objectMapper = new ObjectMapper();
this.objectMapper
.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
}
#Override
public ObjectMapper getContext(Class<?> objectType) {
return objectMapper;
}
}
Bear in mind that formatting has a negative effect on performance.
If you are using Spring, then you can globally set the property
spring.jackson.serialization.INDENT_OUTPUT=true
More info at https://docs.spring.io/spring-boot/docs/current/reference/html/howto-properties-and-configuration.html
Building on helpful DaTroop's answer, here is another version which allows choosing between optimized json and formatted json based on the absence or presence of a "pretty" parameter :
package test;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JacksonContextResolver implements ContextResolver<ObjectMapper> {
private ObjectMapper prettyPrintObjectMapper;
private UriInfo uriInfoContext;
public JacksonContextResolver(#Context UriInfo uriInfoContext) throws Exception {
this.uriInfoContext = uriInfoContext;
this.prettyPrintObjectMapper = new ObjectMapper();
this.prettyPrintObjectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
}
#Override
public ObjectMapper getContext(Class<?> objectType) {
try {
MultivaluedMap<String, String> queryParameters = uriInfoContext.getQueryParameters();
if(queryParameters.containsKey("pretty")) {
return prettyPrintObjectMapper;
}
} catch(Exception e) {
// protect from invalid access to uriInfoContext.getQueryParameters()
}
return null; // use default mapper
}
}
If you are using the jersey-media-json-binding dependency, which uses Yasson (the official RI of JSR-367) and JAVAX-JSON, you can introduce pretty printing as follows:
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
#Provider
public class RandomConfig implements ContextResolver<Jsonb> {
private final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true));
public RandomConfig() { }
#Override
public Jsonb getContext(Class<?> objectType) {
return jsonb;
}
}
Alternative for Jersey 1.x:
org.codehaus.jackson.map.ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);