При разработке сервисов REST с использованием Spring MVC я хотел бы отображать JSON «красиво напечатанным» в разработке, но нормальным (уменьшенным пробелом) в производстве.
При использовании Spring MVC для REST, как вы позволяете Джексону красиво печатать визуализированный JSON?
Ответы (10)
Если вы используете 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
ожидает, что вы внедрите экземпляр Jackson ObjectMapper
и выполните настройку Jackson на этом экземпляре (а не через класс Spring).
Я думал, что это будет так же просто, как сделать это:
Создайте реализацию ObjectMapperFactoryBean
, которая позволит мне настроить экземпляр ObjectMapper
, который можно внедрить в 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>
И затем, в моей реализации 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()
— это просто геттер, совместимый с JavaBeans, с соответствующим сеттером, который я добавил в свой подкласс MappingJacksonHttpMessageConverter
.
Только после прыжков через эти обручи я смог включить или отключить красивую печать на основе моего значения ${json.prettyPrint}
(которое устанавливается как свойство в зависимости от того, как развернуто приложение).
Я надеюсь, что это поможет кому-то в будущем!
Когда вы используете Jackson 2.0.0, вы можете делать это так, как хотел Лес. В настоящее время я использую 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);
- person gertas; 11.02.2013
Могу ли я предложить этот подход, он действителен для 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's Весенний блог
objMapper.registerModule(new JavaTimeModule());
. Я бы приветствовал передовой опыт других.
- person Chris Everitt; 26.09.2016
Как мне заставить Джексона красиво печатать контент 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. Отключите красивую печать в производственной среде.
Конфигурация конвертера сообщений
<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>
prettyPrint
. Как мне этого добиться.
- person mickeymoon; 13.04.2015
Основываясь на 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, поскольку Spring MVC использует ObjectMapper#writeValue(JsonGenerator, Object) для записи объектов в виде JSON. Этот вариант writeValue не применяет функции сериализации ObjectMapper, такие как INDENT_OUTPUT, ни в Jackson 1.x, ни в 2.0.
Я думаю, что это несколько сбивает с толку. Поскольку мы используем ObjectMapper для создания JsonGenerators, я ожидаю, что возвращенные генераторы будут инициализированы на основе настроенных параметров ObjectMapper. Я сообщил об этом как о проблеме с Jackson 2.0 здесь: https://github.com/FasterXML/jackson-databind/issues/12.
Предложение Леса вызвать JsonGenerator#useDefaultPrettyPrinter на основе значения флага prettyPrint — лучшее, что мы можем сделать на данный момент. Я пошел дальше и создал HttpMessageConverter Jackson2, который делает это на основе включенного состояния INDENT_OUTPUT SerializationFeature: https://gist.github.com/2423129.
Я бы сделал это проблемой рендеринга, а не заботой службы REST.
Кто занимается визуализацией? Пусть этот компонент отформатирует JSON. Возможно, это могут быть два URL-адреса — один для производства, а другой для разработки.