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

Posted

技术标签:

【中文标题】当使用 Spring MVC for REST 时,如何让 Jackson 漂亮地打印呈现的 JSON?【英文标题】:When using Spring MVC for REST, how do you enable Jackson to pretty-print rendered JSON? 【发布时间】:2011-09-26 08:56:09 【问题描述】:

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

【问题讨论】:

jira.codehaus.org/browse/JACKSON-128 第 128 期的信息是否回答了这个问题?不是特定于 Jersey 或 @Component 或 @Get 注释,而是直接回答问题标题“如何让 Jackson 漂亮地打印它生成的 JSON 内容?”,我在下面发布了一个答案。 见link 【参考方案1】:

如何让 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" ]
  

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

【讨论】:

恐怕这个例子与我的情况无关。我不直接使用 ObjectMapper 和 ObjectWriter。 这在 spring mvc、jaskcon 的上下文中没有帮助。 对。这解决了这篇文章标题中的具体问题,适合那些偶然发现这些信息的人。 在较新的版本中应该是:ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter(); 此答案未解决问题中指定的环境。【参考方案2】:

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

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

【讨论】:

显然应该是这种情况(并且在我的应用程序中)。我在询问如何启用或禁用该行为的具体接线。 (不过没关系,我有一个答案,我会尽快发布。)【参考方案3】:

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

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

我认为它会像这样做一样简单:

创建一个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 其他地方的解决方案所记录的那样):

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。

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

我希望这对将来的人有所帮助!

【讨论】:

提出改进建议的 JIRA 请求可能是有意义的。【参考方案4】:

我无法按照上面的建议让自定义的 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>

【讨论】:

【参考方案5】:

当您使用 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"
  

【讨论】:

或只是jacksonMapper.enable(SerializationFeature.INDENT_OUTPUT);【参考方案6】:

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

【讨论】:

非常感谢 Keith! 这不再适用。 Jackson 2.1.0 支持 Spring MVC 的 INDENT_OUTPUT 功能。【参考方案7】:

我可以建议这种方法吗,它适用于 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

【讨论】:

+1 用于 JavaConfig 示例。另外,我认为需要使用这种方法手动注册 Java 8 日期和时间支持。我用objMapper.registerModule(new JavaTimeModule()); 做到了这一点。我欢迎其他人在这方面的最佳做法。【参考方案8】:

通过添加和配置 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>

【讨论】:

json.prettyPrint 设置为“true”后适用于 Spring MVC 虽然这解决了我在构建时的问题,但我正在寻找一种解决方案,使用请求参数prettyPrint 呈现/阻止漂亮的打印。我如何做到这一点。【参考方案9】:

如果您使用的是 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测试)

【讨论】:

哇,这很简单。感谢您为我节省了时间。 我在 2011 年问过这个问题,但是根据这个答案,Spring Boot 的版本让这个问题变得更加简单。我正在将已接受的答案更改为这个答案,以便更多人找到它而不是旧答案。【参考方案10】:

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

【讨论】:

以上是关于当使用 Spring MVC for REST 时,如何让 Jackson 漂亮地打印呈现的 JSON?的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC + Spring Security 登录与一个 REST Web 服务

带有angular2的spring mvc(rest)导致302暂时移动

混合 Spring MVC + Spring Data Rest 会导致奇怪的 MVC 响应

Spring MVC REST + Spring Security + 基本身份验证

在 Spring mvc/rest 开发中创建控制器时对以下内容感到困惑:ModelAndView、Model、@ResponseBody、@ResponseEntity

REST - 使用 Spring MVC 返回创建的对象