tutorial restcontroller que funciona example español ejemplo como json spring-mvc jackson pretty-print

restcontroller - Al usar Spring MVC para REST, ¿cómo habilita a Jackson para imprimir bastante JSON?



spring rest json (10)

Mientras desarrollé servicios REST usando Spring MVC, me gustaría renderizar JSON ''bastante impreso'' en desarrollo pero normal (espacio en blanco reducido) en producción.


¿Cómo hago para que Jackson imprima muy bien el contenido JSON que genera?

Aquí hay un ejemplo simple:

Entrada JSON original:

{"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;} }

Salida:

{ "one" : "AAA", "two" : [ "BBB", "CCC" ], "three" : { "four" : "DDD", "five" : [ "EEE", "FFF" ] } }

Si este enfoque no se ajusta exactamente a sus necesidades, si busca en la documentación de la API v1.8.1 "bonito", se mostrarán los componentes relevantes disponibles. Si usa la versión 2.x de la API, busque en su lugar los documentos API 2.1.0 más nuevos .


Basado en baeldung podría ser una buena idea usar 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); } }


Cuando usas Jackson 2.0.0, puedes hacerlo de la manera que Les quería. Actualmente uso RC3 y la configuración parece estar funcionando como se esperaba.

ObjectMapper jacksonMapper = new ObjectMapper(); jacksonMapper.configure(SerializationFeature.INDENT_OUTPUT, true);

traduce

{"foo":"foo","bar":{"field1":"field1","field2":"field2"}}

dentro

{ "foo" : "foo", "bar" : { "field1" : "field1", "field2" : "field2" } }


Haría de eso un problema de renderizado, no la preocupación del servicio REST.

¿Quién está haciendo el renderizado? Permita que ese componente formatee el JSON. Tal vez pueden ser dos URL, una para producción y otra para desarrollo.


Jackson 2 tiene una mejor API, estuvo de acuerdo, pero no resolverá este problema en un entorno Spring MVC dado que Spring MVC usa ObjectMapper # writeValue (JsonGenerator, Object) para escribir objetos como JSON. Esta variante writeValue no aplica características de serialización de ObjectMapper como INDENT_OUTPUT en Jackson 1.xo 2.0.

Yo creo que esto es algo confuso. Como utilizamos ObjectMapper para construir JsonGenerators, esperaría que los generadores devueltos se inicialicen en función de la configuración configurada de ObjectMapper. Informé de esto como un problema contra Jackson 2.0 aquí: https://github.com/FasterXML/jackson-databind/issues/12 .

La sugerencia de Les de llamar a JsonGenerator # useDefaultPrettyPrinter basado en el valor de una bandera de prettyPrint es lo mejor que podemos hacer en este momento. Seguí adelante y creé un Jackson2 HttpMessageConverter que hace esto en función del estado habilitado de INDENT_OUTPUT SerializationFeature: https://gist.github.com/2423129 .


La impresión bonita se habilitará al agregar y configurar el convertidor MappingJackson2HttpMessageConverter . Deshabilite la impresión simplificada dentro del entorno de producción.

Configuración del convertidor de mensajes

<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>


Puedo sugerir este enfoque, es válido con Spring 4.0.xy posiblemente versiones anteriores.

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()); } }

Gracias a Willie Wheeler por la solución: el blog de Willie Wheeler''s Spring


Recibí una respuesta cuando publiqué esta pregunta, pero pensé que la publicaría de todos modos en caso de que hubiera mejores soluciones alternativas. Aquí estaba mi experiencia:

Lo primero es lo primero. El MappingJacksonHttpMessageConverter espera que usted inyecte una instancia de Jackson ObjectMapper y realice la configuración de Jackson en esa instancia (y no a través de una clase Spring).

Pensé que sería tan fácil como hacer esto:

Cree una implementación de ObjectMapperFactoryBean que me permita personalizar la instancia de ObjectMapper que se puede inyectar en MappingJacksonHttpMessageConverter . Por ejemplo:

<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>

Y luego, en mi implementación de ObjectMapperFactoryBean , pude hacer esto (como se ha documentado como una solución en otro lugar en SO):

ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, isPrettyPrint()); return mapper;

Pero no funcionó. Y tratando de descubrir por qué es una pesadilla . Es una gran prueba de paciencia descubrir a Jackson. Mirar su código fuente solo lo confunde aún más, ya que usa formas de configuración obsoletas y obsoletas (máscaras de bits enteras para activar / desactivar funciones? ¿Me está tomando el pelo?)

Básicamente tuve que volver a escribir Spring''s MappingJacksonHttpMessageConverter desde cero y anular su implementación de writeInternal para que sea la siguiente:

@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); } }

Lo único que agregué a la implementación existente es el siguiente bloque:

if (isPrettyPrint()) { jsonGenerator.useDefaultPrettyPrinter(); }

isPrettyPrint() es solo un getter compatible con JavaBeans con setter que he agregado a mi subclase MappingJacksonHttpMessageConverter .

Solo después de pasar por estos aros pude activar o desactivar la impresión bonita en función de mi ${json.prettyPrint} (que se establece como una propiedad según cómo se implemente la aplicación).

¡Espero que esto ayude a alguien en el futuro!


Si está utilizando Spring Boot 1.2 o posterior, la solución simple es agregar

spring.jackson.serialization.INDENT_OUTPUT=true

al archivo application.properties . Esto supone que está usando Jackson para la serialización.

Si está utilizando una versión anterior de Spring Boot, puede agregar

http.mappers.json-pretty-print=true

Esta solución aún funciona con Spring Boot 1.2, pero está en deprecated y eventualmente se eliminará por completo. Obtendrá una advertencia de obsolescencia en el registro en el momento del inicio.

(probado usando spring-boot-starter-web )


Tuve problemas para hacer que el MappingJacksonHttpMessageConverter personalizado funcionara como se sugirió anteriormente, pero finalmente pude hacerlo funcionar después de luchar con la configuración. Desde el punto de vista del código hice exactamente lo que se mencionó anteriormente, pero tuve que agregar la siguiente configuración a mi springapp-servlet.xml para que funcione.

Espero que esto ayude a otros que buscan implementar lo mismo.

<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>