tutorial responseentity responsebody requestbody mvc example ejemplo java json spring-mvc

java - responseentity - spring rest json



Devolviendo JsonObject usando @ResponseBody en SpringMVC (2)

Estoy usando la nueva API de Java (JSR 353) para JSON en un proyecto SpringMVC.

La idea es generar algunos datos de Json y devolverlos al cliente. El controlador que tengo se ve algo así:

@RequestMapping("/test") @ResponseBody public JsonObject test() { JsonObject result = Json.createObjectBuilder() .add("name", "Dade") .add("age", 23) .add("married", false) .build(); return result; }

Y cuando accedo a esto, en lugar de obtener la representación esperada de JSON, obtengo estos:

{"name":{"chars":"Dade","string":"Dade","valueType":"STRING"},"age":{"valueType":"NUMBER","integral":true},"married":{"valueType":"FALSE"}}

¿Por qué es esto? Que esta pasando? ¿Y cómo hago para que devuelva el JSON esperado correctamente?


La respuesta de Sotirios Delimanolis sí funciona, pero en mi caso tuve que asegurarme de que el pedido correcto de HttpMessageConverter estaba en su lugar. Esto se debe a que también necesitaba convertir los valores de JodaTime al formato ISO 8601. Esta configuración personalizada de WebMvcConfigurerAdapter funcionó para mí:

@Configuration public class WebConfiguration extends WebMvcConfigurerAdapter { @SuppressWarnings("UnusedDeclaration") private static final Logger log = LoggerFactory.getLogger(WebConfiguration.class); public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { log.info("Configuring jackson ObjectMapper"); final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); final ObjectMapper objectMapper = new ObjectMapper(); //configure Joda serialization objectMapper.registerModule(new JodaModule()); objectMapper.configure(com.fasterxml.jackson.databind.SerializationFeature. WRITE_DATES_AS_TIMESTAMPS, false); // Other options such as how to deal with nulls or identing... objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); objectMapper.enable(SerializationFeature.INDENT_OUTPUT); converter.setObjectMapper(objectMapper); StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); /* StringHttpMessageConverter must appear first in the list so that Spring has a chance to use it for Spring RestController methods that return simple String. Otherwise, it will use MappingJackson2HttpMessageConverter and clutter the response with escaped quotes and such */ converters.add(stringHttpMessageConverter); converters.add(converter); super.configureMessageConverters(converters); } }


La respuesta es bastante simple cuando te das cuenta de que no hay un HandlerMethodReturnValueHandler especial para la nueva API JSR 353. En su lugar, en este caso, el RequestResponseBodyMethodProcessor (para @ResponseBody ) utiliza un MappingJackson2HttpMessageConverter para serializar el valor de retorno de su método de controlador.

Internamente, MappingJackson2HttpMessageConverter utiliza un ObjectMapper . De forma predeterminada, el ObjectMapper utiliza los captadores de una clase para serializar un objeto a JSON.

Suponiendo que está utilizando la implementación del proveedor de Glassfish de JSR 353, esas clases son org.glassfish.json.JsonObjectBuilderImpl$JsonObjectImpl , org.glassfish.json.JsonStringImpl y org.glassfish.json.JsonNumberImpl y javax.json.JsonValue$3 (una clase anónima para el valor FALSE ).

Debido a que JsonObjectImpl (su resultado, es decir, raíz, objeto) es un Map (tipo especial), ObjectMapper serializa las entradas del mapa como elementos de par de valor-clave JSON, donde la tecla Mapa es la tecla JSON y el valor Mapa es el valor JSON . Para la clave, funciona bien, serializando como name , age y married . Para el valor, utiliza las clases que mencioné anteriormente y sus respectivos captadores. Por ejemplo, org.glassfish.json.JsonStringImpl se implementa como

final class JsonStringImpl implements JsonString { private final String value; public JsonStringImpl(String value) { this.value = value; } @Override public String getString() { return value; } @Override public CharSequence getChars() { return value; } @Override public ValueType getValueType() { return ValueType.STRING; } ... }

ObjectMapper por lo tanto, utiliza a los JsonStringImpl Java Bean para serializar el objeto JsonStringImpl (que es el valor de la Entrada del Mapa), como

{"chars":"Dade","string":"Dade","valueType":"STRING"}

Lo mismo se aplica para los otros campos.

Si desea escribir correctamente el JSON, simplemente devuelva una String .

@RequestMapping("/test", produces="application/json") @ResponseBody public String test() { JsonObject result = Json.createObjectBuilder() .add("name", "Dade") .add("age", 23) .add("married", false) .build(); return result.toString(); }

O haz tu propio HandlerMethodReturnValueHandler , un poco más complicado, pero más gratificante.