在将Spring MVC用于REST时,如何使Jackson能够漂亮地打印呈现的JSON?

| 在使用Spring MVC开发REST服务时,我想在开发过程中渲染JSON \“漂亮打印\”,但在生产过程中呈现正常(减少的空白)。     
已邀请:
        如果您使用的是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上使用,但已过时,最终将被完全删除。在启动时,您将在日志中收到弃用警告。 (使用
spring-boot-starter-web
测试)     
        发布此问题时我有一个答案,但是我想还是应该发布它,以防万一有更好的替代解决方案。这是我的经验: 第一件事是第一位的。
MappingJacksonHttpMessageConverter
希望您注入JacksonJackson5ѭ实例并在该实例上执行Jackson配置(而不是通过Spring类)。 我认为这样做很容易: 创建一个
ObjectMapperFactoryBean
实现,使我能够自定义可以注入
MappingJacksonHttpMessageConverter
中的
ObjectMapper
实例。例如:
<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>
然后,在我的“ 6”实现中,我可以做到这一点(正如在SO上其他地方的解决方案中所述):
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, isPrettyPrint());
return mapper;
但这没有用。并试图弄清楚为什么是一场噩梦。弄清楚杰克逊是耐心的主要考验。查看其源代码只会使您进一步困惑,因为它使用了过时且钝的配置形式(用于打开/关闭功能的整数位掩码?您在开玩笑吗?) 实际上,我必须从头开始重写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()
是我添加到
MappingJacksonHttpMessageConverter
子类中的JavaBeans兼容getter w /匹配setter。 只有跳过这些箍圈之后,我才可以根据我的
${json.prettyPrint}
值(该值设置为属性,取决于应用程序的部署方式)来打开或关闭漂亮的打印。 希望这对以后的人有所帮助!     
        使用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\"
  }
}
    
        可能我建议使用这种方法,它在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的Spring博客     
           如何使Jackson精美打印其生成的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\" ]
  }
}
如果这种方法不能完全满足您的需求,那么如果您在API文档v1.8.1中搜索“漂亮”,它将显示可用的相关组件。如果您使用的是API版本2.x,请查看较新的API 2.1.0文档。     
        通过添加和配置MappingJackson2HttpMessageConverter转换器,可以实现漂亮的打印效果。在生产环境中禁用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>
    
        基于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);
    }
}
    
        我很难像上面建议的那样使自定义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>
    
同意,Jackson 2有一个更好的API,但鉴于Spring MVC使用ObjectMapper#writeValue(JsonGenerator,Object)将对象写为JSON,因此在Spring MVC环境中无法解决此问题。此writeValue变体不适用于Jackson 1.x或2.0中的ObjectMapper序列化功能,例如INDENT_OUTPUT。 我确实认为这有些令人困惑。由于我们使用ObjectMapper构造JsonGenerator,因此我希望返回的生成器将基于配置的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。     
        我会提出一个渲染问题,而不是REST服务的问题。 谁在做渲染?让该组件格式化JSON。可能是两个URL-一个用于生产,另一个用于开发。     

要回复问题请先登录注册