JAX-RS/Джерси .get(Integer.class) и одиночные примитивные значения JSON (Integer)?

У меня есть JAX-RS WebService со следующим методом:

@Path("/myrest")
public class MyRestResource {
...
    @GET
    @Path("/getInteger")
    @Produces(APPLICATION_JSON)
    public Integer getInteger() {
        return 42;
    }

При доступе с помощью этого фрагмента:

@Test
public void testGetPrimitiveWrapers() throws IOException {
    // this works:
    assertEquals(new Integer(42), new ObjectMapper().readValue("42", Integer.class));
    // that fails:
    assertEquals(new Integer(42), resource().path("/myrest/getInteger").get(Integer.class));
}

Я получаю следующее исключение:

com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: A message body reader for Java class java.lang.Integer, and Java type class java.lang.Integer, and MIME media type application/json was not found
com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: The registered message body readers compatible with the MIME media type are: application/json
...

Проблема заключается только в возврате одиночных примитивных значений (int/boolean) или их классов-оболочек. Возврат других классов POJO не является проблемой, поэтому я думаю, что все ответы, касающиеся аннотаций JSONConfiguration.FEATURE_POJO_MAPPING и JAXB, здесь не применяются. Или какую аннотацию я должен использовать для описания возвращаемого типа, если у меня нет доступа к его источнику класса?

Используя ngrep, я могу убедиться, что веб-сервис возвращает только строку «42». Это допустимое «значение» JSON, но не допустимый «текст» JSON в соответствии со спецификацией. Так моя проблема на стороне клиента или на стороне сервера?

Я попытался активировать JSONConfiguration natural/badgerfish согласно http://tugdualgrall.blogspot.de/2011/09/jax-rs-jersey-and-single-element-arrays.html, но безуспешно (ngrep по-прежнему показывает только «42»). Будет ли это правильным путем?

Любые идеи приветствуются!


person lathspell    schedule 06.05.2013    source источник
comment
Важно ли, чтобы тип носителя возвращаемого значения был application/json? Поскольку это просто текст, почему бы не установить @Produces("text/plain"). Если вам действительно нужен тип носителя application/json, вы можете написать собственный MessageBodyWriter.   -  person bdkosher    schedule 07.05.2013


Ответы (1)


Это признанная ошибка в Jackson, которая рекламировалась (неправильно в моем мнение) как признак. Почему я считаю это багом? Потому что, хотя сериализация работает, десериализация определенно не работает.

В любом случае действительный JSON не может быть сгенерирован из вашего текущего возвращаемого типа, поэтому я бы рекомендовал создать класс-оболочку:

class Result<T> {
    private T data;

    // constructors, getters, setters
}

@GET
@Path("/getInteger")
@Produces(APPLICATION_JSON)
public Result<Integer> getInteger() {
    return new Result<Integer)(42);
}

В качестве альтернативы вы можете выбрать перенос корневых значений, что автоматически инкапсулирует ваши данные в объект JSON верхнего уровня с ключами простого типа объектов. имя - но обратите внимание, что если эта опция используется, все сгенерированные JSON будут упакованы (не только для примитивов):

final ObjectMapper mapper = new ObjectMapper()
    .configure(SerializationFeature.WRAP_ROOT_VALUE, true)
    .configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);

final String serializedJson = mapper.writeValueAsString(42);
final Integer deserializedVal = mapper.readValue(serializedJson,
        Integer.class);

System.out.println(serializedJson);
System.out.println("Deserialized Value: " + deserializedVal);

Выход:

{"Целое число":42}
Десериализованное значение: 42

Подробнее о том, как получить и настроить экземпляр ObjectMapper в среде JAX-RS, см. в этом ответе.

person Perception    schedule 07.05.2013
comment
Спасибо! Теперь я наконец понимаю, что значение JSON и текст JSON — это не одно и то же. Но виноват действительно JSON, а не Java :) - person lathspell; 08.05.2013