当使用Spring MVC for REST时,你如何让jackson漂亮地打印呈现的JSON?

在使用Spring MVC开发REST服务的同时,我希望在开发中呈现JSON'漂亮的印刷',但在生产中正常(减less空白)。

如果您使用Spring Boot 1.2或更高版本,则简单的解决scheme是添加

spring.jackson.serialization.INDENT_OUTPUT=true 

application.properties文件。 这假设你正在使用Jackson进行序列化。

如果您使用Spring Boot的早期版本,则可以添加

 http.mappers.json-pretty-print=true 

这个解决scheme仍然适用于Spring Boot 1.2,但不推荐使用 ,最终将被彻底删除。 在启动时您将在日志中获得弃用警告。

(使用spring-boot-starter-web进行testing)

当我发布这个问题时,我有一个答案,但我想我会张贴它,以防有更好的替代解决scheme。 这是我的经验:

第一件事是第一件事 MappingJacksonHttpMessageConverter期望您注入一个Jackson ObjectMapper实例,并在该实例上执行Jacksonconfiguration(而不是通过Spring类)。

我认为这样做会很简单:

创build一个ObjectMapperFactoryBean实现,允许我自定义可以注入到MappingJacksonHttpMessageConverterObjectMapper实例。 例如:

 <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> 

然后,在我的ObjectMapperFactoryBean实现中,我可以做到这一点(在SO上的其他地方已经被logging为解决scheme):

 ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, isPrettyPrint()); return mapper; 

但它没有工作。 并试图找出为什么是一场噩梦 。 jackson是一个耐心的重大考验。 看看它的源代码,只会让你更加混淆,因为它使用过时和钝的configurationforms(用于打开/closuresfunction的整数位掩码?你在开玩笑吗?)

我基本上必须从头开始重写Spring的MappingJacksonHttpMessageConverter ,并覆盖它的writeInternal实现如下:

 @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); } } 

我添加到现有的实现的唯一的东西是下面的块:

 if (isPrettyPrint()) { jsonGenerator.useDefaultPrettyPrinter(); } 

isPrettyPrint()只是一个JavaBean兼容的getter w /匹配设置器,我添加到我的MappingJacksonHttpMessageConverter子类。

只有在跳过这些箍环之后,我才能根据我的${json.prettyPrint}值(根据应用程序的部署方式将其设置为属性)打开或closures漂亮的打印。

我希望这能帮助未来的人!

当你使用Jackson 2.0.0时,你可以用Les想要的方式来做。 我目前使用RC3和configuration似乎按预期工作。

 ObjectMapper jacksonMapper = new ObjectMapper(); jacksonMapper.configure(SerializationFeature.INDENT_OUTPUT, true); 

转换

 {"foo":"foo","bar":{"field1":"field1","field2":"field2"}} 

 { "foo" : "foo", "bar" : { "field1" : "field1", "field2" : "field2" } } 

我如何让jackson打印它生成的JSON内容?

这是一个简单的例子:

原始的JSONinput:

 {"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;} } 

输出:

 { "one" : "AAA", "two" : [ "BBB", "CCC" ], "three" : { "four" : "DDD", "five" : [ "EEE", "FFF" ] } } 

如果这种方法不完全符合您的需求,那么如果您在API文档v1.8.1中search“漂亮”,则会提供相关的组件。 如果您使用API​​版本2.x,那么请查看较新的API 2.1.0文档 。

我可能会build议这种方法,它适用于Spring 4.0.x以及更旧的版本。

 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()); } } 

感谢威利惠勒的解决scheme: 威利惠勒的春季博客

漂亮的打印将通过添加和configurationMappingJackson2HttpMessageConverter转换器启用。 在生产环境中禁用相纸。

消息转换器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> 

我无法让自定义的MappingJacksonHttpMessageConverter按照上面的build议工作,但是我终于能够在configuration后挣扎了。 从代码的angular度来看,我完全是上面提到的,但是我不得不把下面的configuration添加到我的springapp-servlet.xml中来让它工作。

我希望这能帮助那些希望实施相同的人。

 <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有一个更好的API,但是在Spring MVC环境下,Spring MVC使用ObjectMapper#writeValue(JsonGenerator,Object)将对象写成JSON,所以不能解决这个问题。 此writeValue变体不应用Jackson 1.x或2.0中的INDEXECT_OUTPUT等ObjectMapper序列化function。

我认为这有点令人困惑。 由于我们使用ObjectMapper来构buildJsonGenerators,所以我希望返回的生成器可以根据configuration的ObjectMapper设置进行初始化。 我在这里报告这是一个针对jackson2.0的问题: https : //github.com/FasterXML/jackson-databind/issues/12 。

Les基于prettyPrint标志的值调用JsonGenerator#useDefaultPrettyPrinter的build议是我们现在可以做的最好的。 我已经提前创build了一个基于INDENT_OUTPUT SerializationFeature的启用状态的Jackson2 HttpMessageConverter: https ://gist.github.com/2423129。

基于baeldung这可能是一个不错的主意,使用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); } } 

我会做这个渲染问题,而不是REST服务的关注。

谁在做渲染? 让该组件格式化JSON。 也许它可以是两个url – 一个用于生产,另一个用于开发。