2013-08-23 9 views
9

La mia domanda di base: c'è qualcosa di costruito che lo faccia già automaticamente (non deve far parte di una libreria/pacchetto popolare)? Le cose principali su cui sto lavorando sono Spring (MVC) e Jackson2.Come serializzare QUALSIASI Oggetto in un URI?

Capisco che ci sono alcuni modi manuali per farlo:

  1. Creare un metodo in ogni classe che serializza le sue proprietà specifiche in property=value& forma (tipo di puzza, perché è un po 'di duplicazione logica, mi sento).
  2. Creare una funzione che accetta un oggetto e utilizza la riflessione per leggere dinamicamente tutte le proprietà (credo i getter) e creare la stringa ottenendo ciascuna. Suppongo che questo sia il modo in cui Jackson lavora per la serializzazione/deserializzazione in generale, ma davvero non lo so.
  3. Utilizzare alcune funzionalità di Jackson per serializzare l'oggetto in modo personalizzato. Ho cercato serializzatori personalizzati, ma sembra che siano specifici per una classe (quindi dovrei crearne uno per ogni classe che sto cercando di serializzare), mentre speravo in un modo generico. Ho solo difficoltà a capire come applicare universalmente agli oggetti. Alcuni dei collegamenti:
  4. Usa ObjectMapper.convertValue(object, HashMap.class);, iterare su coppie chiave/valore s' il HashMap, e costruire la stringa (che è quello che sto usando ora, ma sento che le conversioni sono eccessive?).
  5. Immagino ci siano altri a cui non penso.

Il post principale che ho guardato in è Java: Getting the properties of a class to construct a string representation

Il mio punto è che ho diverse classi che voglio essere in grado di serializzare senza dover specificare qualcosa di specifico per ciascuno. Ecco perché sto pensando che una funzione che utilizza il reflection (n. 2 sopra) sia l'unico modo per gestirlo (se devo farlo manualmente).

Se aiuta, un esempio di quello che voglio dire è con, diciamo, queste due classi:

public class C1 { 
    private String C1prop1; 
    private String C1prop2; 
    private String C1prop3; 

    // Getters and setters for the 3 properties 
} 

public class C2 { 
    private String C2prop1; 
    private String C2prop2; 
    private String C2prop3; 

    // Getters and setters for the 3 properties 
} 

(no, le proprietà nomi e le convenzioni non quello che il mio attuale applicazione sta usando sono, è solo un esempio)

I risultati della serializzazione sarebbero C1prop1=value&C1prop2=value&C1prop3=value e C2prop1=value&C2prop2=value&C2prop3=value, ma c'è solo una posizione che definisce come avviene la serializzazione (già definita da qualche parte o creata manualmente da me).

Quindi la mia idea è che dovrò finire con una forma del seguente (preso dal post che ho linkato sopra):

public String toString() { 
    StringBuilder sb = new StringBuilder(); 
    try { 
     Class c = Class.forName(this.getClass().getName()); 
     Method m[] = c.getDeclaredMethods(); 
     Object oo; 
     for (int i = 0; i < m.length; i++) 
     if (m[i].getName().startsWith("get")) { 
      oo = m[i].invoke(this, null); 
      sb.append(m[i].getName().substring(3) + ":" 
         + String.valueOf(oo) + "\n"); 
     } 
    } catch (Throwable e) { 
     System.err.println(e); 
    } 
    return sb.toString(); 
} 

e modificarlo per accettare un oggetto, e modificare il formato degli elementi aggiunti allo StringBuilder. Questo funziona per me, non ho bisogno di aiuto per modificare questo ora.

Quindi, ancora una volta, la mia domanda principale è se c'è qualcosa che gestisce già questa (potenzialmente semplice) serializzazione invece di dover modificare (rapidamente) la funzione di cui sopra, anche se devo specificare come gestire ogni proprietà e valore e come combinarli?

Se aiuta, lo sfondo di questo è che sto usando un RestTemplate (Spring) per fare una richiesta GET a un server diverso, e voglio passare le proprietà/i valori di un oggetto specifico nell'URL. Capisco che posso usare qualcosa del tipo:

restTemplate.getForObject("URL?C1prop1={C1Prop1}&...", String.class, C1Object); 

Credo che le proprietà verranno automaticamente mappate. Ma come ho detto, non voglio dover creare un modello di URL e un metodo diversi per ogni tipo di oggetto. Spero di avere qualcosa di simile al seguente:

public String getRequest(String url, Object obj) { 
    String serializedUri = SERIALIZE_URI(obj); 
    String response = restTemplate.getForObject("URL?" + serializedUri, String.class); 
    return response; 
} 

dove SERIALIZE_URI è dove mi piacerebbe gestire la cosa. E potrei chiamarlo come getRequest("whatever", C1Object); e getRequest("whateverElse", C2Object);.

risposta

12

Penso che la soluzione numero 4 sia OK. È semplice da capire e chiarire.

propongo soluzione simile in cui possiamo usare @JsonAnySetter annotazione. Si prega, vedere sotto Esempio:

import com.fasterxml.jackson.annotation.JsonAnySetter; 
import com.fasterxml.jackson.databind.ObjectMapper; 

public class JacksonProgram { 

    public static void main(String[] args) throws Exception { 
     C1 c1 = new C1(); 
     c1.setProp1("a"); 
     c1.setProp3("c"); 

     User user = new User(); 
     user.setName("Tom"); 
     user.setSurname("Irg"); 

     ObjectMapper mapper = new ObjectMapper(); 
     System.out.println(mapper.convertValue(c1, UriFormat.class)); 
     System.out.println(mapper.convertValue(user, UriFormat.class)); 
    } 
} 

class UriFormat { 

    private StringBuilder builder = new StringBuilder(); 

    @JsonAnySetter 
    public void addToUri(String name, Object property) { 
     if (builder.length() > 0) { 
      builder.append("&"); 
     } 
     builder.append(name).append("=").append(property); 
    } 

    @Override 
    public String toString() { 
     return builder.toString(); 
    } 
} 

Sopra programma stampa:

prop1=a&prop2=null&prop3=c 
name=Tom&surname=Irg 

e il metodo di getRequest potrebbe essere la seguente:

public String getRequest(String url, Object obj) { 
    String serializedUri = mapper.convertValue(obj, UriFormat.class).toString(); 
    String response = restTemplate.getForObject(url + "?" + serializedUri, String.class); 
    return response; 
} 
+3

** ** molto interessante e fresco. Ci tornerò sicuramente, a meno che non senta altri suggerimenti. Sono d'accordo # 4 è stato abbastanza buono - immagino che faccia il n. 2 dietro le quinte comunque, quindi potrei anche tenerlo (tranne mi piace molto questa soluzione, quindi la userò :)). – Ian

+0

Mi fa piacere sentirlo. Sentiti libero di usarlo. Jackson è affidabile e veloce quindi penso che dovresti riutilizzare il più possibile il codice sorgente dalla libreria di Jackson. Dovresti anche ricordare la codifica degli URL e altre cose del genere. –

+0

Cool, lo apprezzo. Questa risposta è esattamente ciò che stavo cercando: una caratteristica di Jackson di cui non ero a conoscenza (perché non ne so abbastanza o non utilizzo la biblioteca). Non so se è applicabile, ma sembra che l'uso di '@ JsonView' potrebbe essere un'altra alternativa. Hai qualche idea su come usarlo in questo scenario o perché (non) usarlo in confronto alla tua soluzione? – Ian

2

Lascia abbiamo c1.

c1.setC1prop1("C1prop1"); 
c1.setC1prop2("C1prop2"); 
c1.setC1prop3("C1prop3"); 

Converte c1 in URI

UriComponentsBuilder.fromHttpUrl("http://test.com") 
      .queryParams(new ObjectMapper().convertValue(c1, LinkedMultiValueMap.class)) 
      .build() 
      .toUri()); 

Dopo avremo

http://test.com?c1prop1=C1prop1&c1prop2=C1prop2&c1prop3=C1prop3 
+0

Questo funziona, tuttavia, è utile solo se si utilizza il framework Spring o se si ha accesso a tali librerie. – njfife

Problemi correlati