使用Spring MVC for REST时,如何让Jackson能够打印出漂亮的JSON?

时间:2011-06-30 22:19:48

标签: json spring-mvc jackson pretty-print

在使用Spring MVC开发REST服务时,我想在开发中渲染JSON'漂亮打印',但在生产中正常(缩小空白)。

10 个答案:

答案 0 :(得分:43)

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

spring.jackson.serialization.INDENT_OUTPUT=true

application.properties文件。这假定您使用Jackson进行序列化。

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

http.mappers.json-pretty-print=true

此解决方案仍适用于Spring Boot 1.2,但它是deprecated,最终将完全删除。您将在启动时在日志中收到弃用警告。

(使用spring-boot-starter-web测试)

答案 1 :(得分:31)

当我发布这个问题时,我得到了一个答案,但我认为无论如何我都会发布它,以防有更好的替代解决方案。这是我的经历:

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

我认为这样做很容易:

创建一个ObjectMapperFactoryBean实现,允许我自定义可以注入ObjectMapper的{​​{1}}实例。例如:

MappingJacksonHttpMessageConverter

然后,在我的<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> 实施中,我可以做到这一点(正如在其他地方记录为SO的解决方案):

ObjectMapperFactoryBean

但它没有用。并试图找出为什么是噩梦。杰克逊出局是对耐心的一次重大考验。查看它的源代码只会让你更加困惑,因为它使用过时和钝的配置形式(用于打开/关闭功能的整数位掩码?你在开玩笑吗?)

我基本上不得不从头开始重写Spring ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, isPrettyPrint()); return mapper; ,并覆盖其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(); } 只是一个兼容JavaBeans的getter w /匹配setter,我添加到我的isPrettyPrint()子类中。

只有在跳过这些箍之后,我才能根据我的MappingJacksonHttpMessageConverter值(根据应用程序的部署方式设置为属性)打开或关闭漂亮打印。

我希望将来可以帮助某人!

答案 2 :(得分:24)

当您使用Jackson 2.0.0时,您可以按照Les想要的方式进行操作。 我目前使用RC3,配置似乎按预期工作。

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

翻译

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

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

答案 3 :(得分:22)

  

如何让杰克逊打印出它生成的JSON内容呢?

这是一个简单的例子:

原始JSON输入:

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

如果这种方法不能完全满足您的需求,那么如果您在the API docs v1.8.1搜索“漂亮”,它将会显示相关的可用组件。如果您使用API​​版本2.x,请查看newer API 2.1.0 docs

答案 4 :(得分:22)

我可能会建议这种方法,它适用于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());
    }

}

感谢Willie Wheeler提供的解决方案:Willie Wheeler's Spring blog

答案 5 :(得分:6)

通过添加和配置 MappingJackson2HttpMessageConverter 转换器来启用Pretty print。在生产环境中禁用prettyprint。

邮件转换器配置

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

答案 6 :(得分:4)

基于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);
    }
}

答案 7 :(得分:3)

我无法按照上面的建议让自定义MappingJacksonHttpMessageConverter工作,但我终于能够在配置困难之后让它工作了。从代码的角度来看,我完全按照上面提到的那样做了,但是我必须将以下配置添加到我的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>

答案 8 :(得分:3)

杰克逊2有一个更好的API,同意,但它不会在Spring MVC环境中解决这个问题,因为Spring MVC使用ObjectMapper#writeValue(JsonGenerator,Object)将对象写成JSON。此writeValue变体不在Jackson 1.x或2.0中应用ObjectMapper序列化功能,例如INDENT_OUTPUT。

我认为这有点令人困惑。由于我们使用ObjectMapper来构造JsonGenerators,我希望根据配置的ObjectMapper设置初始化返回的生成器。我在此报告了这是针对Jackson 2.0的问题:https://github.com/FasterXML/jackson-databind/issues/12

Les建议根据prettyPrint标志的值调用JsonGenerator#useDefaultPrettyPrinter是我们目前可以做的最好的。我已经开始创建一个Jackson2 HttpMessageConverter,它根据INDENT_OUTPUT SerializationFeature的启用状态执行此操作:https://gist.github.com/2423129

答案 9 :(得分:1)

我认为这是一个渲染问题,而不是REST服务的关注点。

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