2013-10-06 12 views
16

Sto utilizzando la nuova API Java (JSR 353) per JSON in un progetto SpringMVC.Restituzione di JsonObject tramite @ResponseBody in SpringMVC

L'idea è generare parte dei dati Json e restituirli al client. Il controller ho guardare 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 questo, invece di ottenere la rappresentazione atteso del JSON, ottengo questi invece:

{"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?

+2

Che API è questo? –

+0

@SotiriosDelimanolis di API, ti riferisci a JsonObject? è la JSR 353: API Java per l'elaborazione JSON.Domanda aggiornata anche – dade

risposta

22

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

Internamente, lo MappingJackson2HttpMessageConverter utilizza uno ObjectMapper. Per impostazione predefinita, lo ObjectMapper utilizza i getter di una classe per serializzare un oggetto su JSON.

Supponendo che si sta utilizzando Glassfish s' implementazione del provider della JSR 353, le classi sono org.glassfish.json.JsonObjectBuilderImpl$JsonObjectImpl, org.glassfish.json.JsonStringImpl, e org.glassfish.json.JsonNumberImpl, e javax.json.JsonValue$3 (una classe anonima per il valore FALSE).

Perché JsonObjectImpl (il risultato, vale a dire. Radice, oggetto) è una (tipo speciale) Map, ObjectMapper serializza le voci della mappa come elementi JSON coppia chiave-valore, dove la chiave mappa è la chiave di JSON, e il valore Mappa è il valore JSON. Per la chiave, funziona perfettamente, 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 utilizza quindi i getter Java Bean per serializzare l'oggetto JsonStringImpl (ovvero il valore di mappa Entry), come

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

Lo stesso vale per gli altri campi .

Se si desidera scrivere correttamente il JSON, è sufficiente restituire un 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 fare il vostro proprio HandlerMethodReturnValueHandler, un po 'più complicato, ma più gratificante.

+3

Ma il JSON risultante viene scappato, come '" {\ "foo \": \ "bar \"} "', come farlo restituire solo JSON, come '{" foo ":" bar "}'? –

+0

@DzmitryLazerka È scappato dove? Il metodo 'test()' qui sopra non sfugge a nulla. –

+0

Nel convertitore di messaggi Spring, dopo il test restituito. Vede che il risultato è una stringa e che l'applicazione/json di tipo mime deve essere convertita in JSON. Vedi MappingJackson2HttpMessageConverter (è aggiunto per impostazione predefinita se Jackson è presente in WebMvcConfigurationSupport). Quindi converte la stringa in JSON, facendola sfuggire e aggiungendo le virgolette doppie. –

1

La risposta di Sotirios Delimanolis funziona davvero, ma nel mio caso dovevo assicurarmi che l'ordine HttpMessageConverter corretto fosse a posto. Questo perché avevo bisogno di convertire anche i valori di JodaTime nel formato ISO 8601. Questa configurazione personalizzata di WebMvcConfigurerAdapter 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<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); 
} 
} 
Problemi correlati