Как сериализовать примитивы Java с помощью Jersey REST

В своем приложении я использую Jersey REST для сериализации сложных объектов. Это прекрасно работает. Но есть несколько методов, которые просто возвращают int или boolean.

Джерси не может обрабатывать примитивные типы (насколько мне известно), вероятно, потому, что они не аннотированы, а Джерси не имеет для них аннотаций по умолчанию. Я работал над этим, создавая сложные типы, такие как RestBoolean или RestInteger, которые просто содержат целочисленное или логическое значение и имеют соответствующие аннотации.

Нет ли более простого способа, чем написание этих контейнерных объектов?


person Olvagor    schedule 13.04.2010    source источник
comment
JAX-RS/Jersey не поддерживает сериализацию примитивных типов или даже типов-оболочек, таких как Integer, Boolean и т. д. AFAIK, подход, который вы выбрали, кажется единственным способом.   -  person Suresh Kumar    schedule 13.04.2010


Ответы (6)


Взгляните на Genson. Это очень помогло мне с похожей проблемой. С Genson вы можете использовать дженерики, такие как int, boolean, lists и так далее... Вот быстрый пример.

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getMagicList() {
    List<Object> objList = new ArrayList<>();
    stringList.add("Random String");
    stringList.add(121); //int
    stringList.add(1.22); //double
    stringList.add(false); //bolean

    return Response.status(Status.OK).entity(objList).build();
}

Это создаст действительный файл JSON, который можно получить очень просто, например:

    Client client = Client.create();
    WebResource webResource = client.resource("...path to resource...");
    List objList = webResource.accept(MediaType.APPLICATION_JSON).get(ArrayList.class);
    for (Object obj : objList) {
        System.out.println(obj.getClass());
    }

Вы увидите, что Genson поможет вам декодировать JSON также на стороне клиента и вывести правильный класс для каждого.

person mvlupan    schedule 03.02.2013

Вы пишете сервис или клиент? В конце обслуживания вы должны просто написать MessageBodyWriter для сериализации потока данных в объект Java для ваших типов. В моих случаях использования сервисы, которые я пишу, выводятся в JSON или XML, а в случае с XML я просто добавляю одну аннотацию JAXB поверх своих классов, и все готово.

Вы смотрели руководство пользователя Джерси по этому поводу?

3.6. Добавление поддержки новых представлений

person Nick Klauer    schedule 29.04.2010

На самом деле лучше всего написать собственный провайдер ContextResolver, как показано ниже, который использует естественное построение JSON.

   @Provider
   public class YourContextResolver implements ContextResolver<JAXBContext> {

    private JAXBContext context;
    private Class<?>[] types = { YourSpecialBean.class };

    public YourContextResolver() throws Exception {
        this.context = new JSONJAXBContext(
                JSONConfiguration.natural().build(), types);
    }

    public JAXBContext getContext(Class<?> objectType) {
        for (int i = 0; i < this.types.length; i++)
            if (this.types[i].equals(objectType)) return context;

        return null;
    }
}

Единственное, на что следует обратить внимание, — это YourSpecialBean.class в Class[]. Это определяет массив типов классов, которые этот провайдер будет разрешать естественным образом.

person Alex Winston    schedule 20.05.2010

Скажите Джерси сгенерировать правильные документы JSON (естественный json). Я использую тот же класс для приложения для отдыха и преобразователь JAXBContext, нашел его наиболее чистой инкапсуляцией.

Лучший программист мог бы реализовать помощника для повторения файлов .class и автоматического перечисления соответствующих классов, определяя теги @Annotation. Я не знаю, как это сделать во время выполнения в собственном исходном коде.

Эти две ссылки были полезны для изучения этого дополнительного жаргона Java. Я не знаю, почему нет параметра Джерси, чтобы все работало из коробки.

WEB-INF/web.xml (фрагмент):

<servlet>
  <servlet-name>RESTServlet</servlet-name>
  <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>javax.ws.rs.Application</param-name>
    <param-value>com.myapp.rest.RESTApplication</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>RESTServlet</servlet-name>
  <url-pattern>/servlet/rest/*</url-pattern>
</servlet-mapping>

com.myapp.rest.RESTApplication.java

package com.myapp.rest;

import java.util.*;
import javax.ws.rs.core.Application;
import javax.ws.rs.ext.ContextResolver;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.api.json.JSONJAXBContext;

public class RESTApplication extends Application implements ContextResolver<JAXBContext> {
    private JAXBContext context;
    private Class<?>[] types;

    public RESTApplication() throws JAXBException {
        // list JAXB bean types to be used for REST serialization
        types = new Class[] {
            com.myapp.rest.MyBean1.class, 
            com.myapp.rest.MyBean2.class, 
        };
        context = new JSONJAXBContext(JSONConfiguration.natural().build(), types);
    }

    @Override
    public Set<Class<?>> getClasses() {
        // list JAXB resource/provider/resolver classes
        Set<Class<?>> classes = new HashSet<Class<?>>();
        //for(Class<?> type : types)
        //    classes.add(type);
        classes.add(MyBeansResource.class);
        classes.add(this.getClass()); // used as a ContextResolver class
        return classes;
    }

    @Override
    public JAXBContext getContext(Class<?> objectType) {
        // this is called each time when rest path was called by remote client
        for (Class<?> type : types) {
            if (type==objectType)
                return context;
        }
        return null;
    }
}

Классы MyBean1, MyBean2 являются простыми объектами Java, а класс MyBeansResource — это класс с остальными функциями @Path. В них нет ничего особенного, за исключением стандартных jaxp @Annotations здесь и там. После этого java-жаргона документы JSON имеют

  • Нулевые или одноэлементные массивы List всегда записываются как массив json (поле [])
  • примитивные целые числа и логические поля записываются как примитивы json (без кавычек)

Я использую следующую среду

  • Sun Java JDK1.6.x
  • Apache Tomcat 6.x
  • Библиотеки Jersey v1.14 (jersey-archive-1.14.zip)
  • В папке webapps/myapp/WEB-INF/lib есть asm-3.3.1.jar, jackson-core-asl.jar, jersey-client.jar, jersey-core.jar, jersey-json.jar, jersey-server.jar , библиотеки jersey-servlet.jar
  • добавьте необязательный annotation-detector.jar, если вы используете инструмент обнаружения infomas-asl

В jersey-archive.zip был старый файл asm-3.1.jar, возможно, он работает нормально, но глава_deps.html ссылается на более новый файл. Смотрите список ссылок вверху.

Редактировать Я нашел отличный (быстрый, легкий, всего 15 КБ) инструмент для обнаружения аннотаций. См. этот пост о том, как я автоматически обнаруживаю типы во время выполнения и больше не нужно редактировать RESTApplication каждый раз, когда добавляется новый компонент java (jaxb).

https://github.com/rmuller/infomas-asl/issues/7

person Whome    schedule 27.09.2012

Сегодня у меня была такая же проблема, и я не сдавался, пока не нашел действительно хорошее подходящее решение. Не могу обновить библиотеку джерси с 1.1.5 это Legacy System. Моя служба отдыха возвращает список, и они должны следовать этим правилам.

  1. Пустые списки отображаются как [] (почти невозможно)
  2. Списки одного элемента отображаются как [] (сложная, но только конфигурация отображения)
  3. Многие списки элементов отображаются как [] (просто)

Начните с простого к невозможному.

3) ничего сегодня нормального JSON Mapping

2) Зарегистрируйте JAXBContextResolver, как показано ниже.

@Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
    private final JAXBContext context;
    private final Set<Class<?>> types;
    private Class<?>[] ctypes = { Pojo.class }; //your pojo class
    public JAXBContextResolver() throws Exception {
        this.types = new HashSet<Class<?>>(Arrays.asList(ctypes));
        this.context = new JSONJAXBContext(JSONConfiguration.mapped()
                .rootUnwrapping(true)
                .arrays("propertyName") //that should rendered as JSONArray even if the List only contain one element but doesn't handle the empty Collection case
                .build()
                , ctypes);
    }

    @Override
    public JAXBContext getContext(Class<?> objectType) {
        return (types.contains(objectType)) ? context : null;
    }
}

1) Следующий подход работает только для класса Collections$EmptyList. Можете ли вы найти способ сделать это общим для всех коллекций, которые они пусты. Может ли код работать с EmptyList так.

@Provider
@Produces(value={MediaType.APPLICATION_JSON})
public class EmptyListWriter implements MessageBodyWriter<AbstractList> {

    private static final String EMPTY_JSON_ARRAY = "[]";

    @Override
    public long getSize(AbstractList list, Class<?> clazz, Type type, Annotation[] annotations, MediaType mediaType) {
        return EMPTY_JSON_ARRAY.length();
    }

    @Override
    public boolean isWriteable(Class<?> clazz, Type type, Annotation[] annotations, MediaType mediaType) {
        return clazz.getName().equals("java.util.Collections$EmptyList");
    }

    @Override
    public void writeTo(AbstractList list, Class<?> clazz, Type type, Annotation[] annotations, MediaType mediaType, 
            MultivaluedMap<String, Object> headers, OutputStream outputStream) throws IOException, WebApplicationException {
        if (list.isEmpty())
            outputStream.write(EMPTY_JSON_ARRAY.getBytes());            
    }
}
person Frank Ittermann    schedule 08.01.2016

Я только что обнаружил, что возврат примитивного типа с помощью Джерси проблематичен. Вместо этого я решил вернуть String. Может быть, это не чисто, но я не думаю, что это слишком грязно. Клиент Java, который в большинстве случаев пишет тот же автор сервера, может обернуть такое строковое возвращаемое значение и преобразовать его обратно в int. Клиенты, написанные на других языках, должны в любом случае знать о возвращаемых типах.

Определение RestInteger, RestBoolean может быть другим вариантом, однако он более громоздкий, и я не вижу в нем слишком мало преимуществ, чтобы быть привлекательным.

Или, может быть, я упускаю здесь что-то важное?

person zakmck    schedule 07.10.2013
comment
на мой взгляд, это определенно не должно быть таким громоздким и включаться в класс трикотажа. Я сделал тот же вывод в воскресенье в 10 вечера, я просто разберусь с чертовой строкой ха. - person JesseBoyd; 30.10.2017