2010-04-13 25 views
8

W mojej aplikacji korzystam z usługi Jersey REST w celu przekształcania do postaci szeregowej złożonych obiektów. Działa to całkiem dobrze. Ale istnieje kilka metod, które po prostu zwracają wartość int lub boolean.Jak serializować prymitywy Java za pomocą usługi REST aplikacji Jersey REST

Jersey nie może obsłużyć typów pierwotnych (według mojej wiedzy), prawdopodobnie dlatego, że nie ma adnotacji i Jersey nie ma adnotacji domyślnej dla nich. Pracowałem nad tym, tworząc skomplikowane typy, takie jak RestBoolean lub RestInteger, które po prostu trzymają wartość int lub boolean i mają odpowiednie adnotacje.

Czy nie ma łatwiejszego sposobu niż pisanie tych obiektów kontenerowych?

+3

JAX-RS/Jersey nie obsługuje serializacji prymitywnych typów lub nawet typów otoki takich jak Integer, Boolean itd AFAIK, podejście zażycia wydaje się być jedynym sposobem. –

+0

Okay, dziękuję! – Olvagor

Odpowiedz

3

Czy piszesz usługę lub klienta? Pod koniec usługi wystarczy napisać numer MessageBodyWriter, aby szeregować strumień danych do obiektu Java dla typów. W moich przypadkach korzystania z usług, które piszę, wyprowadzam do JSON lub XML, a w przypadku XML po prostu umieszczam jedną adnotację JAXB na moich zajęciach i gotowe.

Czy zapoznałeś się z przewodnikiem użytkownika Jersey w tej sprawie?

3.6. Adding support for new representations

2

Faktycznie najlepiej jest napisać Provider zwyczaj ContextResolver jak poniżej, który używa natural budynek 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; 
    } 
} 

Jedyne, co można tutaj zauważyć, to klasa YourSpecialBean.class w klasie []. To definiuje tablicę typów klas, które ten dostawca rozwiąże w sposób naturalny.

2

Tell Jersey generuje odpowiednie dokumenty JSON (natural json). Używam tej samej klasy dla aplikacji odpoczynku i resolwera JAXBContext, uznałem ją za najbardziej czystą enkapsulację.

Lepszy programista może zaimplementować pomocnika do iterowania plików .class i automatycznie wymieniać odpowiednie klasy, identyfikując @ znaczniki adnotacji. Nie wiem jak to zrobić w czasie wykonywania we własnym kodzie źródłowym.

Te dwa linki pomogły w przestudiowaniu tego dodatkowego żargonu java. Nie wiem, dlaczego nie ma parametru Jersey, który sprawiłby, że wszystko po prostu wyszło z pudełka.

WEB INF/web.xml (fragment)

<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; 
    } 
} 

Klasy MyBean1, MyBean2 są obiektami Java ślizgowe i klasy MyBeansResource jedną z @Path funkcji odpoczynku. Nie ma w nich niczego wyjątkowego, oczekując standardowego jaxp @Annotations tu i tam.Po tym java żargonie dokumentów JSON mieć

  • zero lub tablice Lista pojedynczych elementów są zawsze zapisywane jako json tablicy ([] polowym)
  • prymitywne całkowitymi i pola boolowskie są zapisywane jako prymitywów json (bez cytatów)

używam Poniżej przedstawiono

  • Sun Java JDK1.6.x
  • Apache Tomcat 6.x
  • biblioteki Jersey v1.14 (jersey-archive-1.14.zip)
  • webapps/myapp/WEB-INF/lib folderu ma ASM-3.3.1.jar, Jackson-core-asl.jar, jersey-client JAR, jersey-core.jar, jersey-json.jar, jersey-server.jar, biblioteki Jersey servlet.jar
  • dodać opcjonalny adnotacji-detector.jar jeśli używasz infomas-ASL narzędzia Discovery

jersey-archive.zip ma starszy plik asm-3.1.jar, prawdopodobnie działa dobrze, ale chapter_deps.html linki do nowszego pliku. Zobacz listę linków u góry.

Edycja Znalazłem doskonałe (szybkie, lekkie tylko 15KB) narzędzie do wykrywania adnotacji. Zobacz ten post na temat tego, jak automatycznie wykrywać typy w czasie wykonywania i nie trzeba już edytować RESTApplication za każdym razem, gdy dodawany jest nowy komponent bean java (jaxb).

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

4

spojrzeć Genson .To bardzo mi pomogło z podobnym problem.With Genson można użyć rodzajowych, takich jak int, boolean, list i tak dalej ... Oto krótki przykład.

@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(); 
} 

To będzie produkować ważnego czarownicę JSON mogą być pobierane bardzo prosty tak:

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()); 
    } 

Zobaczysz, że Genson pomoże dekodowania JSON po stronie klienta także i wysyłać prawidłowe klasy dla każdego.

+0

Poprawny link html do projektu Genson to http://code.google.com/p/genson/ – Whome

2

Właśnie odkryłem, że powrót typ prymitywny z Jersey jest problematyczny. Postanowiłem zamiast tego zwrócić String. Może to nie jest czyste, ale nie sądzę, że jest zbyt brudne. Klient Java, który jest pisany przez tego samego autora serwera najczęściej, może zawijać taką wartość zwracaną przez ciąg i przekonwertować ją z powrotem na int. Klienci napisani w innych językach muszą znać rodzaje zwrotów w dowolny sposób.

Definiowanie RestInteger, RestBoolean może być inną opcją, jednak jest bardziej uciążliwa i widzę za mało korzyści, aby być atrakcyjną.

A może brakuje mi czegoś ważnego tutaj?

+1

to zdecydowanie nie powinno być tak uciążliwe i zaliczone do klasy dżersejów w mojej opinii. Podobny wniosek wyciągnąłem w niedzielę o 22.00. Zajmę się właśnie cekiną ha. – JesseBoyd

2

Miałem ten sam problem dzisiaj i nie poddawałem się, dopóki nie znalazłem naprawdę dobrego odpowiedniego rozwiązania. Nie mogę zaktualizować biblioteki jersey z wersji 1.1.5, jest to starszy system. Moja usługa spoczynku zwraca listę i powinny one przestrzegać tych reguł.

  1. pustych list są renderowane jako [] (prawie niemożliwe) Listy
  2. jeden element jest wyświetlany jako [
  3. wiele list elementu są wyświetlane jako [] (łatwe)] (trudne, ale tylko konfiguracja mapowania)

Rozpocznij od łatwego do niemożliwego.

3) nic już normalny JSON Mapowanie

2) Rejestracja JAXBContextResolver jak na poniższym

@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) następujące podejście działa tylko dla klasy Collections $ EmptyList. Możesz znaleźć sposób, by był ogólny dla wszystkich kolekcji, które są puste. Może tak traktować kod z 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());    
    } 
}