Как сериализовать примитивы Java с помощью Jersey REST - PullRequest
8 голосов
/ 13 апреля 2010

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

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

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

Ответы [ 6 ]

4 голосов
/ 03 февраля 2013

Взгляните на 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 на стороне клиента и вывести правильный класс для каждого.

3 голосов
/ 29 апреля 2010

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

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

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

2 голосов
/ 08 января 2016

У меня была та же проблема сегодня, и я не сдавался, пока не нашел действительно хорошее подходящее решение. Я не могу обновить библиотеку Джерси с 1.1.5, это устаревшая система. My Rest Service возвращает список, и они должны следовать этим правилам.

  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());            
    }
}
2 голосов
/ 07 октября 2013

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

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

Или, может, я что-то упустил здесь?

2 голосов
/ 27 сентября 2012

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

Лучший программист может реализовать помощник для итерации файлов .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 здесь и там. После этого JSON-документы java jargon имеют

  • ноль или одноэлементные списочные массивы всегда записываются в виде массива 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, вероятно, работает нормально, но chapter_deps.html ссылается на более новый файл. См. Список ссылок вверху.

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

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

2 голосов
/ 21 мая 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 в классе []. Это определяет массив типов классов, которые этот провайдер будет разрешать естественным образом.

...