当使用 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
实现,它允许我自定义可以注入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>
然后,在我的 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