2013-10-06 3 views
16

Я использую новый Java API (JSR 353) для JSON в проекте SpringMVC.Возвращение JsonObject с помощью @ResponseBody в SpringMVC

Идея состоит в том, чтобы сгенерировать часть данных Json и вернуть ее клиенту. Контроллер у меня выглядело примерно так:

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

И когда я получить доступ к этому, вместо того, чтобы получать ожидаемое представление JSON, я получаю это вместо:

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

Почему это? Что происходит? И как мне заставить его правильно вернуть ожидаемый JSON?

+2

Какой API это? –

+0

@SotiriosDelimanolis по API, вы имеете в виду JsonObject? это JSR 353: Java API для обработки JSON.Вопрос также обновлен – dade

ответ

22

Ответ довольно прост, когда вы понимаете, что нет специального HandlerMethodReturnValueHandler для нового API JSR 353. Вместо этого в этом случае RequestResponseBodyMethodProcessor (для @ResponseBody) использует MappingJackson2HttpMessageConverter для сериализации возвращаемого значения вашего метода обработчика.

Внутренне, MappingJackson2HttpMessageConverter использует ObjectMapper. По умолчанию ObjectMapper использует геттеры класса для сериализации объекта в JSON.

Предполагая, что вы используете Glassfish «s реализации поставщика в JSR 353, эти классы org.glassfish.json.JsonObjectBuilderImpl$JsonObjectImpl, org.glassfish.json.JsonStringImpl и org.glassfish.json.JsonNumberImpl и javax.json.JsonValue$3 (анонимный класс для значения FALSE).

Поскольку JsonObjectImpl (ваш результат, то есть. Корень, объект) является Map (специальный типа), ObjectMapper упорядочивает запись карты в качестве элементов JSON ключ-значения пары, где ключ карты является ключом JSON, и значения карт является значением JSON. Для ключа он отлично работает, сериализуется как name, age и married. Для значения используются классы, упомянутые выше, и их соответствующие геттеры. Например, org.glassfish.json.JsonStringImpl реализован как

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 поэтому использует добытчик Java Bean для сериализации JsonStringImpl объекта (то есть значение карты запись вцелом), а

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

То же самое относится и для других полеев ,

Если вы хотите правильно написать JSON, просто верните 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(); 
} 

Или сделать свой собственный HandlerMethodReturnValueHandler, немного сложнее, но более полезным.

+3

Но в результате JSON получает escape-последовательность, например '' {\ "foo \": \ "bar \"} "', как заставить его возвращать только JSON, например '{" foo ":" bar "}'? –

+0

@DzmitryLazerka Сбежал где? Вышеупомянутый метод 'test()' не ускользает. –

+0

В преобразователе сообщений Spring, после того, как тест вернулся. Он видит, что результатом является String, а для mime-type application/json необходимо преобразовать в JSON. См. MappingJackson2HttpMessageConverter (он добавляется по умолчанию, если Джексон присутствует в WebMvcConfigurationSupport). Таким образом, он преобразует строку в JSON, избегая ее и добавляя двойные кавычки. –

1

Ответ от Sotirios Delimanolis действительно работает, но в моем случае я должен был обеспечить надлежащий заказ HttpMessageConverter на месте. Это связано с тем, что мне также необходимо преобразовать значения JodaTime в формат ISO 8601. Эта настраиваемая конфигурация WebMvcConfigurerAdapter для меня работала:

@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); 
} 
} 
Смежные вопросы