При использовании Spring MVC для REST, как вы позволяете Джексону красиво печатать визуализированный JSON?

При разработке сервисов REST с использованием Spring MVC я хотел бы отображать JSON «красиво напечатанным» в разработке, но нормальным (уменьшенным пробелом) в производстве.


person Les Hazlewood    schedule 30.06.2011    source источник
comment
jira.codehaus.org/browse/JACKSON-128   -  person Bozho    schedule 30.05.2011
comment
Отвечает ли информация в выпуске 128 на вопрос? Не конкретно для Джерси или аннотаций @Component или @Get, но в прямом ответе на заголовок вопроса «Как заставить Джексона красиво печатать контент JSON, который он генерирует?», я разместил ответ ниже.   -  person Programmer Bruce    schedule 15.06.2011
comment
См. ссылку.   -  person Marco Lackovic    schedule 14.03.2013


Ответы (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)

person user4061342    schedule 20.09.2014
comment
Вау, это было просто. Спасибо, что сэкономили мне время. - person Jeff French; 14.05.2015
comment
Я задал этот вопрос в 2011 году, но версии Spring Boot сделали это намного проще, согласно этому ответу. Я меняю принятый ответ на этот, чтобы больше людей находили его, а не старый. - person Les Hazlewood; 27.10.2019

У меня был ответ, когда я разместил этот вопрос, но я подумал, что все равно опубликую его, если есть лучшие альтернативные решения. Вот мой опыт:

Перво-наперво. 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} (которое устанавливается как свойство в зависимости от того, как развернуто приложение).

Я надеюсь, что это поможет кому-то в будущем!

person Les Hazlewood    schedule 30.06.2011
comment
Возможно, имеет смысл открыть запрос JIRA, предлагающий это улучшение. - person Oliver Drotbohm; 19.04.2012

Когда вы используете 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"
  }
}
person Swato    schedule 25.03.2012
comment
или просто 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 Весенний блог

person MattJ    schedule 08.05.2014
comment
+1 за пример JavaConfig. Отдельно стоит отметить, что думаю, что при таком подходе поддержку даты и времени Java 8 необходимо регистрировать вручную. Я сделал это с 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. документы.

person Programmer Bruce    schedule 14.06.2011
comment
Боюсь, этот пример не имеет отношения к моему делу. Я не использую ObjectMapper и ObjectWriter напрямую. - person neu242; 15.06.2011
comment
это не поможет в контексте spring mvc, jaskcon. - person Bobo; 22.07.2011
comment
Верно. Это касается конкретного вопроса в заголовке этого сообщения для людей, которые наткнулись на это, ожидая такой информации. - person Programmer Bruce; 22.07.2011
comment
Должно быть в более новых версиях: ObjectWriter author = mapper.writerWithDefaultPrettyPrinter(); - person Eelco; 14.06.2012
comment
Этот ответ не относится к среде, указанной в вопросе. - person Les Hazlewood; 27.02.2014
comment
Да, я вижу, что вопрос был изменен. Конечно, первоначальный вопрос заключался в том, как заставить Джексона красиво печатать контент JSON, который он генерирует? Мой пост действительно отвечает на этот оригинальный вопрос. - person Programmer Bruce; 10.03.2014

Прекрасную печать можно включить, добавив и настроив преобразователь 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>
person Ben Asmussen    schedule 30.05.2014
comment
{json.prettyPrint} после установки значения true работает с Spring MVC - person fortm; 13.12.2014
comment
в то время как это решает мою проблему во время сборки, но я ищу решение, в котором красивая печать отображается/предотвращается с использованием параметра запроса 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);
    }
}
person davidwillianx    schedule 16.07.2016

У меня были проблемы с тем, чтобы пользовательский 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>
person user977505    schedule 22.03.2012

Согласен, у 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.

person kdonald    schedule 19.04.2012
comment
Большое спасибо за это Кит! - person Les Hazlewood; 19.04.2012
comment
Это больше не применимо. Jackson 2.1.0 поддерживает функцию INDENT_OUTPUT в Spring MVC. - person Ryan Walls; 20.02.2013

Я бы сделал это проблемой рендеринга, а не заботой службы REST.

Кто занимается визуализацией? Пусть этот компонент отформатирует JSON. Возможно, это могут быть два URL-адреса — один для производства, а другой для разработки.

person duffymo    schedule 30.06.2011
comment
очевидно, так и должно быть (и это есть в моем приложении). Я просил конкретную проводку того, как включить или отключить поведение. (Это нормально, у меня есть ответ, который я опубликую в ближайшее время.) - person Les Hazlewood; 01.07.2011