2009-11-02 12 views
32

Odgadywałem mój tyłek, próbując dowiedzieć się, jak to zrobić: Mam usługę REST na Jersey. Żądanie wywołujące usługę REST zawiera obiekt JSON. Moje pytanie brzmi: od wdrożenia metody Jersey POST, w jaki sposób mogę uzyskać dostęp do JSON, który znajduje się w treści żądania HTTP?Zużywanie obiektu JSON w serwisie Jersey

Wszelkie wskazówki, porady, wskazówki do przykładowego kodu byłyby bardzo mile widziane.

Dzięki ...

--Steve

+0

Mam do czynienia z podobnym problemem. czy istnieje jakieś rozwiązanie do korzystania z niskiego poziomu dostępu do JSONObject? – Jugi

Odpowiedz

10

Nie jestem pewien, jak można dostać w ciągu samego JSON, ale na pewno można dostać w niej danych w następujący sposób:

Należy zdefiniować klasę Java (C) o adnotacjach JAXB, która ma taką samą strukturę jak obiekt JSON przekazywany w żądaniu.

np. dla wiadomości JSON:

{ 
    "A": "a value", 
    "B": "another value" 
} 

użyć czegoś takiego:

@XmlAccessorType(XmlAccessType.FIELD) 
public class C 
{ 
    public String A; 
    public String B; 
} 

Następnie można zdefiniować metodę w swojej klasie zasobów z parametrem typu C. Po Jersey wywołuje metodę, obiekt JAXB zostaną utworzone na podstawie obiektu POSTed JSON.

@Path("/resource") 
public class MyResource 
{ 
    @POST 
    public put(C c) 
    { 
    doSomething(c.A); 
    doSomethingElse(c.B); 
    } 
} 
+2

Niestety, ciąg JSON jest w zasadzie używany bardziej jako słownik niż jako prawdziwe POJO. Wolałbym nie tworzyć nowego POJO dla obiektów JSON. – Steve

+1

Czy spojrzałeś na użycie MessageBodyReader (http://jackson.codehaus.org/javadoc/jax-rs/1.0/javax/ws/rs/ext/MessageBodyReader.html). Nie wypróbowałem tego samemu, ale możesz być w stanie wejść na ten poziom i przekonwertować strumień JSON na mapę itp. – Andy

+4

W rzeczywistości okazuje się, że jest to znacznie prostsze niż myślałem. Mogę pobrać treść żądania HTTP jako argument łańcuchowy, a następnie przetworzyć go za pomocą dowolnej biblioteki JSON (obecnie korzystającej z Google GSON) w celu przekonwertowania danych żądania na obiekty lokalne. – Steve

0

Prześlij/POST formularz/HTTP.POST z parametrem z JSON jako wartością.

@QueryParam jsonString

desolveJson publicznych (jsonString)

+1

Myślałem o tym. Po prostu nie jest tak, jak chciałbym. Istnieje kilka zestawów narzędzi, które upuszczają kompletny JSON do rzeczywistej treści żądania HTTP i jeśli to możliwe, chciałbym zastosować ten wzór. – Steve

6

To daje dostęp do surowego postu.

@POST 
@Path("/") 
@Consumes("text/plain") 
@Produces(MediaType.APPLICATION_JSON) 
public String processRequset(String pData) { 
    // do some stuff, 
    return someJson; 
} 
+1

To był prosty i łatwy sposób przekazania JSON z mojego klienta i nie ma potrzeby, aby serwer/klient wiedział o obiektach, które przechodzę. Po prostu użyłem GSON, aby je przekształcić i dołączyć do postu. Pracował świetnie! Dzięki. – bh5k

14

Jak już sugeruje, zmieniając @Consumes Content-Type do text/plain będzie działać, ale nie wydaje się słuszne z punktu widzenia REST API.

Wyobraź sobie, że twój klient musi POST JSON do swojego API, ale musi określić nagłówek Content-Type jako text/plain. To nie jest czyste w mojej opinii. Mówiąc prościej, jeśli twój interfejs API akceptuje JSON, wówczas nagłówek żądania powinien określać Content-Type: application/json.

Aby zaakceptować JSON, ale serializować go do obiektu String, a nie POJO, można zaimplementować niestandardowe MessageBodyReader. Zrobienie tego w ten sposób jest równie proste i nie musisz iść na kompromis w sprawie specyfikacji interfejsu API.

Warto przeczytać dokumenty dla MessageBodyReader, dzięki czemu wiesz dokładnie, jak to działa. To jak to zrobiłem:

Krok 1. Wdrożenie zwyczaj MessageBodyReader

@Provider 
@Consumes("application/json") 
public class CustomJsonReader<T> implements MessageBodyReader<T> { 
    @Override 
    public boolean isReadable(Class<?> type, Type genericType, 
     Annotation[] annotations,MediaType mediaType) { 
    return true; 
    } 

    @Override 
    public T readFrom(Class<T> type, Type genericType, Annotation[] annotations, 
     MediaType mediaType, MultivaluedMap<String, String> httpHeaders, 
     InputStream entityStream) throws IOException, WebApplicationException { 

    /* Copy the input stream to String. Do this however you like. 
    * Here I use Commons IOUtils. 
    */ 
    StringWriter writer = new StringWriter(); 
    IOUtils.copy(entityStream, writer, "UTF-8"); 
    String json = writer.toString(); 

    /* if the input stream is expected to be deserialized into a String, 
    * then just cast it 
    */ 
    if (String.class == genericType) 
     return type.cast(json); 

    /* Otherwise, deserialize the JSON into a POJO type. 
    * You can use whatever JSON library you want, here's 
    * a simply example using GSON. 
    */ 
    return new Gson().fromJson(json, genericType); 
    } 
}

Podstawowa koncepcja powyżej jest sprawdzenie, czy oczekuje się, że strumień wejściowy być konwertowane do String (określony przez Type genericType).Jeśli tak, po prostu obsadź JSON w podanym type (który będzie String). Jeśli oczekiwanym typem jest POJO, użyj biblioteki JSON (np. Jackson lub GSON), aby przekształcić ją w obiekt POJO.

Krok 2. powiązać MessageBodyReader

To zależy od tego, co ramy używasz. Uważam, że Guice i Jersey dobrze ze sobą współpracują. Oto jak wiążę moich MessageBodyReader w Guice:

W moim JerseyServletModule wiążę czytelnik jak tak -

bind(CustomJsonReader.class).in(Scopes.SINGLETON);

Powyższy CustomJsonReader będzie deserializowania ładunki JSON język POJOs jak również, jeśli chcesz po prostu surowy Obiekty JSON, String.

Zaletą zrobienia tego w ten sposób jest zaakceptowanie Content-Type: application/json. Innymi słowy, obsługi żądania można ustawić zużywają JSON, co wydaje się właściwe:

@POST 
@Path("/stuff") 
@Consumes("application/json") 
public void doStuff(String json) { 
    /* do stuff with the json string */ 
    return; 
}
11

Jersey obsługuje niskopoziomowy dostęp do przeanalizowanej JSONObject używając typów odrzucić JSONObject i JSONArray.

<dependency> 
    <groupId>org.codehaus.jettison</groupId> 
    <artifactId>jettison</artifactId> 
    <version>1.3.8</version> 
</dependency> 

Na przykład:

{ 
    "A": "a value", 
    "B": "another value" 
} 


@POST 
@Path("/") 
@Consumes(MediaType.APPLICATION_JSON) 
public void doStuff(JSONObject json) { 
    /* extract data values using DOM-like API */ 
    String a = json.optString("A"); 
    Strong b = json.optString("B"); 
    return; 
} 

Zobacz Jersey documentation więcej przykładów.

+4

To nie działa dla mnie. Otrzymuję ... 'Nierozpoznane pole" A "(klasa org.json.JSONObject), niezaznaczone jako niezrozumiałe' ... i jeśli dodaję następujące ... ' mapper.configure (DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); ' ... Następnie obiekt przechodzi przez puste' {} ' – crowmagnumb

+0

to nie działa. Otrzymuję błąd 415. Czy istnieje jakieś rozwiązanie i jaka zależność należy dodać, aby uzyskać niski dostęp json? – Jugi