2009-07-01 13 views
28

Ho un servizio REST JAX-RS implementato utilizzando Jersey. Una delle fantastiche funzionalità di JAX-RS/Jersey è la facilità con cui un POJO può essere trasformato in un servizio REST, semplicemente aggiungendo alcune annotazioni Java ... compreso un meccanismo banalmente semplice per la conversione dei POJO in JSON - usando le annotazioni JAXB.Come riutilizzare JSON/JAXB di Jersey per la serializzazione?

Ora, mi piacerebbe essere in grado di sfruttare questa fantastica funzionalità di JSON-ifying per scopi non REST - Mi piacerebbe essere in grado di serializzare alcuni di questi oggetti su disco, come testo JSON. Ecco un oggetto JAXB esempio, che avrei voluto di serializzare:

@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 può trasformare uno di questi in JSON, senza informazioni aggiuntive. Mi chiedo se Jersey abbia esposto questa funzionalità nell'API per esigenze come la mia? Non ho avuto fortuna a trovarlo finora ...

Grazie!

UPDATE 2009-07-09: ho imparato che posso usare i fornitori di oggetto per quasi faccio esattamente quello che voglio:

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

    uw.writeTo(....) 

... Questo scrive l'oggetto come JSON per qualsiasi outputstream, che sarebbe perfetto per me, ma posso solo ottenere l'oggetto Provider utilizzando @Context da un oggetto @Component. Qualcuno sa come accedervi da un POJO regolare e non annotato? Grazie!

+0

Sto cercando di trovare informazioni anche su questo, ma stavo cercando qualcosa che utilizza solo javax. */Java. * Solo API (Non mi dispiace aggiungere ulteriori librerie per test di JUnit, ma mi aspetterei che essere presente nel JEE6 RI –

+0

Sto anche bene chiamando una classe per il bootstrap. –

risposta

19

Jersey utilizza un paio di strutture diverse a seconda che si usi la notazione mappata(), badgerfish() o natural(). Di solito è naturale quello che la gente vuole. E questo è implementato utilizzando il molto buono (e molto veloce) processore Jackson JSON standalone, credo, che va da Object-> JAXB-> JSON. Tuttavia Jackson fornisce anche il proprio provider JAX-RS per andare direttamente a Object-> JSON.

Infatti, hanno persino aggiunto il supporto per le annotazioni JAXB.Date un'occhiata a

http://wiki.fasterxml.com/JacksonJAXBAnnotations

Penso che sia in ultima analisi, ciò che si sta cercando. Jackson fa Object < -> elaborazione JSON ... Jersey fa solo le chiamate per voi

+1

http://jackson.codehaus.org/ per il progetto del jackson home –

+0

Grazie, Andy. Questo suona davvero come quello che sto cercando. Non voglio semplicemente aggiungere dipendenze aggiuntive non necessarie. Grazie! – ctwomey

+2

Bene, pensa in questo modo: hai bisogno di una libreria per la serializzazione XML (JAXB, XStream o simile). È necessaria una libreria JSON per JSON: JAXB non la fornisce; Jersey lo invia anche a una biblioteca. Quindi la domanda è piuttosto quale lib (s) aggiungere; non se è necessario aggiungere qualcosa. Quindi Jackson (o json-tools, gson) può farlo facilmente. E i provider JAX-RS sono davvero poco più di dispatcher che operano su tipi di media, scegliendo quale "vista" presentare (json, xml, ...), quindi chiamando la libreria appropriata. – StaxMan

1

Poiché Jersey è un'implementazione di riferimento di JAX-RS e JAX-RS si concentra completamente sul fornire un modo standard di implementare l'end-point per il servizio REST, i problemi di serializzazione del carico utile sono lasciati ad altri standard.

Penso che se includessero la serializzazione degli oggetti nello standard JAX-RS diventerebbe rapidamente una grande bestia multi-testata che sarebbe difficile da implementare e perdere parte del suo focus.

Apprezzo la concentrazione di Jersey nel fornire endpoint REST puliti e semplici da utilizzare. Nel mio caso ho appena creato una sottoclasse di un genitore che ha tutte le tubature JAXB al suo interno, quindi gli oggetti di marshalling tra binario e XML sono molto puliti.

+0

Non penso di essere stato molto chiaro, il fatto è che Jersey già traduce gli oggetti JAXB in JSON. aggiungere il supporto per la "serializzazione", ma piuttosto per ottenere l'accesso alla funzionalità Provider già presente per il mio * codice di serializzazione * .Aggiornerò la mia domanda per aggiungere un po 'di chiarezza Grazie! – ctwomey

0

Capisco le viste XML, ma avrebbe mostrato alcune previsioni per richiedere il supporto JSON per POJO come equipaggiamento standard. Dover diagnosticare gli identificatori JSON con caratteri speciali non ha senso se la tua implementazione è JSON e il tuo client è un RIA JavaScript.

Inoltre, non che Java Beans NON siano POJO. Vorrei usare qualcosa di simile sulla superficie esterna del mio livello Web:

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

No costruttore di default, non getter/setter rumore, solo un POJO con i miei annotazioni.

+0

Non penso che questo sia l'errore JAXB però: JAXB è API per XML, non per altri formati, ma la domanda è se l'API JAXB debba essere piegata per funzionare per altri formati, in tal caso sono necessari work-around. XML! = JSON. Btw: Jackson che è già stato menzionato consente l'utilizzo di POJO non Bean; può usare campi o getter/setter, usare effettivi costruttori reali e così via. Può usare le annotazioni JAXB come informazioni extra opzionali se l'utente lo desidera davvero. Ma questo non ha bisogno di loro. – StaxMan

3

JAXB le annotazioni funzionano bene durante la serializzazione in XML. Il problema principale è che JAXB non supporta gli array vuoti. Così, quando la serializzazione qualcosa di simile ...

List myArray = new ArrayList(); 

... a JSON tramite anottations JAXB tutti gli array vuoti diventano nulli invece di [].

Per risolvere questo problema, è possibile serializzare i propri pojos direttamente in JSON tramite Jackson.

Date un'occhiata a questo dal manuale d'uso di Jersey: http://jersey.java.net/nonav/documentation/latest/user-guide.html#d0e1959

questo è il modo migliore per utilizzare provider di Jackson senza JAXB. Inoltre, puoi sempre utilizzare l'ultima versione di Jackson eseguendo il downlaoding di jackson-all-x.y.z-jar dal suo web.

Questo metodo non interferirà con le annotazioni jaxb, quindi suggerirei di provare!

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

Con un piccolo bootstrap specifico di Jersey, è possibile utilizzarlo per creare gli oggetti JSON necessari. È necessario includere le seguenti dipendenze (si può usare bundle, ma sarà causare problemi se si sta utilizzando per il test di saldatura):

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

da lì si può creare una classe JAXB annotata. Quanto segue è un esempio:

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

Quindi è possibile creare la seguente prova di unità:

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\"")); 

Il vantaggio di questo approccio è che mantiene tutto all'interno della API JEE6, senza librerie esterne sono esplicitamente necessari ad eccezione per testare e ottenere i provider. Tuttavia, è necessario creare un'implementazione di MultivaluedMap poiché non esiste nulla nello standard e in realtà non lo usiamo. È potrebbe anche essere più lento di GSON e molto più complicato del necessario.

Problemi correlati