Pytanie w sprawie json, jersey, jax-rs – Jak ponownie wykorzystać JSON / JAXB firmy Jersey do serializacji?

28

Mam usługę REST JAX-RS zaimplementowaną przy użyciu Jersey. Jedną z ciekawszych funkcji JAX-RS / Jersey jest to, jak łatwo można przekształcić POJO w usługę REST, po prostu dodając kilka adnotacji Java ... w tym trywialnie prosty mechanizm tłumaczenia POJO na JSON - przy użyciu adnotacji JAXB.

Teraz chciałbym móc skorzystać z tej fajnej funkcji JSON-ifying do celów innych niż REST - chciałbym móc po prostu serializować niektóre z tych obiektów na dysk, jako tekst JSON. Oto przykładowy obiekt JAXB, który chciałbym serializować:

@XmlRootElement(name = "user")
public class UserInfoImpl implements UserInfo {

    public UserInfoImpl() {} 

    public UserInfoImpl(String user, String details) {
        this.user = user;
        this.details = details;
    }

    public String getUser() { return user; }
    public void setUser(String user) { this.user = user; }

    public String getDetails() { return details; }
    public void setDetails(String details) { this.details = details; }

    private String user;
    private String details;
}

Jersey może zamienić jedną z nich w json bez dodatkowych informacji. Zastanawiam się, czy Jersey ujawniła tę funkcjonalność w API dla potrzeb takich jak moje? Do tej pory nie miałem szczęścia znaleźć tego ...

Dzięki!

AKTUALIZACJA 2009-07-09: Nauczyłem się, że mogę użyć obiektu Providers doprawie Zrób dokładnie to, co chcę:

  @Context Providers ps;
  MessageBodyWriter uw = ps.getMessageBodyWriter(UserInfoImpl.class, UserInfoImpl.class, new Annotation[0], MediaType.APPLICATION_JSON_TYPE);

  uw.writeTo(....)

... Zapisuje obiekt jako json do dowolnego strumienia wyjściowego, który byłby idealny dla mnie, ale mogę uzyskać tylko obiekt Providers używając @Context z obiektu @Component. Czy ktoś wie, jak uzyskać do niego dostęp za pomocą zwykłego POJO bez adnotacji? Dzięki!

Próbuję też znaleźć informacje na ten temat, ale szukałem czegoś, co używa tylko javax. * / Java. * API (nie mam nic przeciwko dodaniu dodatkowych bibliotek do testowania JUnit, ale spodziewałbym się, że będą obecne w JEE6 RI Archimedes Trajano
Jestem także w porządku z wywoływaniem klasy do ładowania początkowego. Archimedes Trajano

Twoja odpowiedź

7   odpowiedzi
19

Jersey używa kilku różnych frameworków w zależności od tego, czy używasz notacji mapped (), badgerfish (), czy natural (). Naturalny jest zazwyczaj tym, czego ludzie chcą. I to jest zaimplementowane przy użyciu bardzo dobrego (i bardzo szybkiego) samodzielnego procesora Jackson JSON, jak sądzę, który pochodzi z Object-> JAXB-> JSON. Jednak Jackson zapewnia również własnego dostawcę JAX-RS, aby przejść bezpośrednio Object-> JSON.

W rzeczywistości dodali nawet obsługę adnotacji JAXB. Spójrz na

http://wiki.fasterxml.com/JacksonJAXBAnnotacje

Myślę, że to jest właśnie to, czego szukasz. Jackson przetwarza Object <-> JSON ... Jersey po prostu dzwoni do ciebie

jackson.codehaus.org na projekt domu jackson Andy O
Dzięki, Andy. To rzeczywiście brzmi jak to, czego szukam. Po prostu nie chcę dodawać niepotrzebnych dodatkowych zależności. Dzięki! ctwomey
Pomyśl o tym w ten sposób: potrzebujesz biblioteki do serializacji XML (JAXB, XStream lub podobne). Potrzebujesz biblioteki JSON dla JSON: JAXB jej nie zapewnia; Jersey wysyła go również do biblioteki. Pytanie brzmi raczej, które lib (s) dodać; nie czy musisz coś dodać. Jackson (lub json-tools, gson) może to łatwo zrobić. A dostawcy JAX-RS to naprawdę niewiele więcej niż dyspozytorzy działający na typach mediów, wybierając „widok” do przedstawienia (json, xml, ...), a następnie wywołując odpowiednią bibliotekę. StaxMan
1

Ponieważ Jersey jest referencyjną implementacją JAX-RS, a JAX-RS koncentruje się całkowicie na dostarczeniu standardowego sposobu implementacji punktu końcowego dla usługi REST, kwestie serializacji ładunku są pozostawione innym standardom.

Myślę, że gdyby uwzględnili serializację obiektów w standardzie JAX-RS, szybko stałaby się to wielkogłowa bestia, która byłaby trudna do zaimplementowania i straciłaby część jej ostrości.

Doceniam, w jaki sposób Jersey jest skoncentrowany na dostarczaniu czystych i prostych w użyciu punktów końcowych REST. W moim przypadku właśnie podklasowałem rodzica, który ma w nim wszystkie instalacje JAXB, więc rozdzielanie obiektów między binarne i XML jest bardzo czyste.

Nie sądzę, bym był bardzo jasny. Rzecz w tym, że Jersey już tłumaczy obiekty JAXB na JSON. Nie szukam Jersey, aby dodać obsługę „serializacji”, ale raczej uzyskać dostęp do funkcjonalności dostawcy, którą już ma dla mojegoposiadać kod serializacji. Zaktualizuję moje pytanie, aby dodać jasności. Dzięki! ctwomey
3

problemem jest to, że JAXB nie obsługuje pustych tablic. Więc podczas serializowania czegoś takiego ...

List myArray = new ArrayList();

... do json przez anotacje jaxb wszystkie puste tablice stają się puste zamiast [].

Aby rozwiązać ten problem, możesz po prostu serializować swoje pojęcia bezpośrednio do jsona przez jacksona.

Spójrz na to z przewodnika użytkownika Jersey:http://jersey.java.net/nonav/documentation/latest/user-guide.html#d0e1959

To najlepszy sposób na użycie dostawcy Jacksona bez JAXB. Co więcej, zawsze możesz użyć najnowszej wersji jacksona, ściągając jackson-all-x.y.z-jar ze swojej sieci.

Ta metoda nie będzie kolidować z adnotacjami jaxb, więc proponuję spróbować!

5
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(pojoObject);
6

Oto prosty przykład użycia JAXB do mapowania obiektów do JSON (używając Jacksona):

http://ondra.zizka.cz/stranky/programovani/java/jaxb-json-jackson-howto.texy

0

ale pokazałoby pewne przewidywanie, by wymagać obsługi JSON dla POJO jako standardowego wyposażenia. Konieczność uzgadniania identyfikatorów JSON ze znakami specjalnymi nie ma sensu, jeśli implementacją jest JSON, a klient jest RIA JavaScript.

Nie, że Java Beans NIE są POJO. Chciałbym użyć czegoś takiego na zewnętrznej powierzchni mojej warstwy internetowej:

public class Model
{
   @Property height;
   @Property weight;
   @Property age;
}

Brak domyślnego konstruktora, szumu pobierającego / ustawiającego, tylko POJO z własnymi adnotacjami.

Nie sądzę jednak, że jest to błąd JAXB: JAXB jest API dla XML, a nie dla innych formatów. Ale pytanie brzmi, czy API JAXB powinno być zgięte, aby pracować dla innych formatów - jeśli tak, potrzebne są obejścia. XML! = JSON. Btw: Jackson, o którym już wspomniano, pozwala na użycie POJO nie-Bean; może używać pól lub pobierających / ustawiających, używać rzeczywistych rzeczywistych konstruktorów i tak dalej. Może używać adnotacji JAXB jako dodatkowych informacji opcjonalnych, jeśli użytkownik naprawdę tego chce. Ale to ich nie potrzebuje. StaxMan
1

iezbędnych obiektów JSON. Musisz dołączyć następujące zależności (możesz użyć pakietu, ale spowoduje to problemy, jeśli używasz Weld do testowania):

    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-json</artifactId>
        <version>1.12</version>
    </dependency>
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.12</version>
    </dependency>

Stamtąd możesz utworzyć klasę z komentarzem JAXB. Oto przykład:

@XmlRootElement
public class TextMessage {
private String text;
    public String getText() { return text; }
    public void setText(String s) { this.text = text; }
}

Następnie możesz utworzyć następujący test jednostkowy:

    TextMessage textMessage = new TextMessage();
    textMessage.setText("hello");
    textMessage.setUuid(UUID.randomUUID());

    // Jersey specific start
    final Providers ps = new Client().getProviders();
    // Jersey specific end
    final MultivaluedMap<String, Object> responseHeaders = new MultivaluedMap<String, Object>() {

        @Override
        public void add(final String key, final Object value) {
        }

        @Override
        public void clear() {
        }

        @Override
        public boolean containsKey(final Object key) {
            return false;
        }

        @Override
        public boolean containsValue(final Object value) {
            return false;
        }

        @Override
        public Set<java.util.Map.Entry<String, List<Object>>> entrySet() {
            return null;
        }

        @Override
        public List<Object> get(final Object key) {
            return null;
        }

        @Override
        public Object getFirst(final String key) {
            return null;
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public Set<String> keySet() {
            return null;
        }

        @Override
        public List<Object> put(final String key, final List<Object> value) {
            return null;
        }

        @Override
        public void putAll(
                final Map<? extends String, ? extends List<Object>> m) {
        }

        @Override
        public void putSingle(final String key, final Object value) {
        }

        @Override
        public List<Object> remove(final Object key) {
            return null;
        }

        @Override
        public int size() {
            return 0;
        }

        @Override
        public Collection<List<Object>> values() {
            return null;
        }
    };

    final MessageBodyWriter<TextMessage> messageBodyWriter = ps
            .getMessageBodyWriter(TextMessage.class, TextMessage.class,
                    new Annotation[0], MediaType.APPLICATION_JSON_TYPE);
    final ByteArrayOutputStream baos = new ByteArrayOutputStream();
    Assert.assertNotNull(messageBodyWriter);

    messageBodyWriter.writeTo(textMessage, TextMessage.class,
            TextMessage.class, new Annotation[0],
            MediaType.APPLICATION_JSON_TYPE, responseHeaders, baos);
    final String jsonString = new String(baos.toByteArray());
    Assert.assertTrue(jsonString.contains("\"text\":\"hello\""));

Zaletą tego podejścia jest to, że zachowuje wszystko w interfejsie API JEE6, żadne zewnętrzne biblioteki nie są wyraźnie potrzebne, z wyjątkiem testowania i pozyskiwania dostawców. Jednak musisz utworzyć implementację MultivaluedMap, ponieważ standard nie zawiera niczego, a my jej nie używamy. Tomoże również być wolniejszy niż GSON i dużo bardziej skomplikowany niż to konieczne.

Powiązane pytania