Restituzione di JsonObject con @ResponseBody in SpringMVC

Sto usando la nuova API Java (JSR 353) per JSON in un progetto SpringMVC.

L’idea è di generare qualche pezzo di dati Json e farlo restituire al client. Il controller mi sembra un po ‘come questo:

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

E quando accedo a questo, invece di ottenere la rappresentazione prevista del JSON, ottengo invece questi:

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

Perchè è questo? Cosa sta succedendo? E come faccio a restituire correttamente il JSON previsto?

La risposta è piuttosto semplice quando ti rendi conto che non esiste uno speciale HandlerMethodReturnValueHandler per la nuova API JSR 353. Invece, in questo caso, RequestResponseBodyMethodProcessor (per @ResponseBody ) utilizza un MappingJackson2HttpMessageConverter per serializzare il valore di ritorno del metodo del gestore.

Internamente, MappingJackson2HttpMessageConverter utilizza un ObjectMapper . Per impostazione predefinita, ObjectMapper utilizza i getter di una class per serializzare un object su JSON.

Supponendo che si stia utilizzando l’implementazione del provider Glassfish JSR 353, tali classi sono org.glassfish.json.JsonObjectBuilderImpl$JsonObjectImpl , org.glassfish.json.JsonStringImpl e org.glassfish.json.JsonNumberImpl e javax.json.JsonValue$3 (una class anonima per il valore FALSE ).

Poiché JsonObjectImpl (il tuo risultato, ovvero root, object) è una Map (tipo speciale), ObjectMapper serializza le voci della mappa come elementi della coppia chiave-valore JSON, dove la chiave Map è la chiave JSON e il valore Map è il valore JSON . Per la chiave, funziona bene, serializzando come name , age e married . Per il valore, utilizza le classi che ho menzionato sopra e i rispettivi getter. Ad esempio, org.glassfish.json.JsonStringImpl è implementato come

 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 pertanto utilizza i getter Java Bean per serializzare l’object JsonStringImpl (ovvero il valore della voce Mappa), come

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

Lo stesso vale per gli altri campi.

Se si desidera scrivere correttamente il JSON, è sufficiente restituire 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(); } 

Oppure crea il tuo HandlerMethodReturnValueHandler , un po ‘più complicato, ma più gratificante.

La risposta di Sotirios Delimanolis funziona davvero, ma nel mio caso dovevo assicurarmi che fosse presente l’ordine HttpMessageConverter corretto. Questo perché avevo bisogno di convertire anche i valori di JodaTime nel formato ISO 8601. Questa configurazione WebMvcConfigurerAdapter personalizzata ha funzionato per me:

 @Configuration public class WebConfiguration extends WebMvcConfigurerAdapter { @SuppressWarnings("UnusedDeclaration") private static final Logger log = LoggerFactory.getLogger(WebConfiguration.class); public void configureMessageConverters(List> 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); } }