Когда вы используете Spring MVC для REST, как вы позволяете Джексону красиво печатать визуализированный JSON? - PullRequest
45 голосов
/ 01 июля 2011

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

Ответы [ 10 ]

43 голосов
/ 20 сентября 2014

Если вы используете Spring Boot 1.2 или более поздней версии, простое решение - добавить

spring.jackson.serialization.INDENT_OUTPUT=true

в файл application.properties. Это предполагает, что вы используете Джексона для сериализации.

Если вы используете более раннюю версию Spring Boot, вы можете добавить

http.mappers.json-pretty-print=true

Это решение все еще работает с Spring Boot 1.2, но оно устарело и в конечном итоге будет полностью удалено Вы получите предупреждение об устаревании в журнале во время запуска.

(проверено с использованием spring-boot-starter-web)

31 голосов
/ 01 июля 2011

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

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

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

24 голосов
/ 25 марта 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"
  }
}
22 голосов
/ 09 мая 2014

Могу ли я предложить этот подход, он действителен для 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());
    }

}

Спасибо Вилли Уилеру за решение: Блог Вилли Уилера Spring

22 голосов
/ 14 июня 2011

Как сделать так, чтобы Джексон печатал содержимое 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 docs v1.8.1 по запросу "pretty", это приведет к появлению соответствующих доступных компонентов.Если вы используете API версии 2.x, посмотрите вместо этого более новую версию API 2.1.0 docs .

6 голосов
/ 30 мая 2014

Хорошая печать будет включена, добавив и настроив конвертер 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>
4 голосов
/ 16 июля 2016

На основании 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);
    }
}
3 голосов
/ 19 апреля 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.Я сообщил об этом как о проблеме с Джексоном 2.0 здесь: https://github.com/FasterXML/jackson-databind/issues/12.

Предложение Лесом вызвать JsonGenerator # useDefaultPrettyPrinter, основанное на значении флага prettyPrint, - это лучшее, что мы можем сделать в данный момент.Я пошел дальше и создал Jackson2 HttpMessageConverter, который делает это на основе включенного состояния INDENT_OUTPUT SerializationFeature: https://gist.github.com/2423129.

3 голосов
/ 22 марта 2012

У меня были проблемы с тем, чтобы заставить настраиваемый 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>
1 голос
/ 01 июля 2011

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

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

...