2008-11-30 19 views
5

Ho riscontrato questo problema in cui, gli oggetti di ibernazione sulla serializzazione producono xml non previsti contenenti tutto il codice strumentato da Hibernate.Problema con la serializzazione degli oggetti Hibernate usando XStream

Abbiamo fatto un po 'di pulizia dell'oggetto prima di serializzare l'oggetto.

Ma, c'è una opzione standard disponibile per serializzare direttamente l'oggetto?

+0

Forse si potrebbe mostrare come si presenta l'XML? –

risposta

3

Non ho usato XStream prima, ma ho serializzato le entità gestite da Hibernate. Non è divertente

ci sono due grandi questioni:

  • pigro carico;
  • Relazioni uno-a-molti.

Il primo è ovvio: è necessario disporre dei dati effettivi per la serializzazione. Il secondo è meno: qualsiasi relazione uno-a-molti dichiarata contro le interfacce di raccolta (ad esempio: Set<T>) verrà collegata alle implementazioni di raccolta di Hibernate (non serializzabile!). Questo potrebbe essere il punto in cui le classi di Hibernate stanno sanguinando nei tuoi oggetti.

ho finito per scrivere codice riflettente (in realtà introspettiva) che ha fatto questo:

  1. con la sessione aperta, ha toccato l'intero grafo di oggetti per forzare-caricare qualsiasi entità scaricati;
  2. Chiusa la sessione di sospensione (incluse le transazioni che riguardano la sua connessione);
  3. Ha camminato sul grafico dell'oggetto, sostituendo qualsiasi elenco, set o mappa con istanze di ArrayList, HashSet o HashMap (raccolte serializzabili conosciute).

Nota che passo 2 è importante - se si sostituiscono le collezioni prima di chiudere la sessione, Hibernate basta mettere le proprie collezioni a destra di nuovo su di chiuso ...

Edit: @ cliff.meyers ho individuato un dettaglio dell'implementazione che ho dimenticato di menzionare: se lo fai, devi limitare il grafo degli oggetti camminando solo alle tue entità e osserva i percorsi di riferimento circolari (es: memorizzando nella cache i riferimenti agli oggetti che hai già camminato).

1

Ci sono alcune informazioni (e codice di esempio) rispetto al Codehaus JIRA:

http://jira.codehaus.org/browse/XSTR-226

abbiamo scritto alcuni strumenti per risolvere questo tipo di problema per un mucchio di altre implementazioni di comunicazione remota (Axis 1, Blaze DS, ecc.). Ciò che abbiamo fatto è molto simile alla soluzione di Dan, anche se abbiamo aggiunto la possibilità di dichiarare quali percorsi oggetto percorrere e quali "snipare" perché in molte situazioni non eravamo interessati a tutti i dati; avrebbe anche causato gravi problemi con il problema "n + 1 seleziona" che si verificava migliaia di volte! :) Penso che implementare un convertitore XStream sarebbe l'approccio ottimale dal momento che dovresti solo percorrere il grafico degli oggetti una volta. Se imposti FlushMode.MANUAL sulla tua sessione, dovresti anche essere in grado di modificare il grafo degli oggetti man mano che fai senza che Hibernate faccia qualcosa di brutto. Usalo con cautela perché è una tecnica piuttosto avanzata.

+0

Whoops, ho dimenticato di menzionare inizialmente i tipi di oggetti limitanti in cui scendere (la tua sintassi di snip) - grazie per la correzione ... –

1

Ho affrontato un problema simile anche se Im non si sta utilizzando l'ibernazione.Ho guardato l'enitity-pot, ma non è proprio quello che stavo cercando perché stavo cercando una soluzione più semplice.

Mi è venuta in mente una soluzione molto semplice che utilizzava la riflessione che deprezzava CGLIB migliorando gli oggetti come un incantesimo.

per favore visita http://www.anzaan.com/2010/06/serializing-cglib-enhanced-proxy-into-json-using-xstream/ per esempio e codice.

3

Ho trovato una soluzione abbastanza sufficiente. Nella mia applicazione, solo PersistentSet faceva casino nell'XML generato da XStream. Così ho aggiunto un altro convertitore di Xstream (che corre con aperta Hibernate Session e oggetti in tempo reale):

XStream xs = new XStream(); 
xs.registerConverter(new CollectionConverter(xs.getMapper()) { 
    @Override 
    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { 
     org.hibernate.collection.PersistentSet ps = (PersistentSet) source; 
     super.marshal(new HashSet(ps), writer, context); 
    } 

    @Override 
    public boolean canConvert(Class type) { 
     return type.isAssignableFrom(org.hibernate.collection.PersistentSet.class); 
    } 
}, XStream.PRIORITY_VERY_HIGH); 
String s = xs.toXML(processInstance); 

L'XML serializzato si presenta come di seguito:

<processLogs class="org.hibernate.collection.PersistentSet"> 
    <pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog> 
     <id>813017</id> 
     <entryDate> 
     <time>1310832421216</time> 
     <timezone>GMT</timezone> 
     </entryDate> 
     <eventI18NKey>process.log.action-performed</eventI18NKey> 
     <additionalInfo>Wydrukuj wniosek</additionalInfo> 
     <logValue>GENERATE_APPLICATION</logValue> 
     <logType>PERFORM_ACTION</logType> 
     <state reference="../../../definition/states/pl.net.bluesoft.rnd.processtool.model.config.ProcessStateConfiguration[8]"/> 
     <processInstance reference="../../.."/> 
     <user reference="../../../creator"/> 
    </pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog> 
    <pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog> 
     <id>808211</id> 
     <entryDate> 
     <time>1310828206169</time> 
     <timezone>GMT</timezone> 
     </entryDate> 
     <eventI18NKey>process.log.action-performed</eventI18NKey> 
     <additionalInfo>Zaakceptuj</additionalInfo> 
     <logValue>ACCEPT</logValue> 
     <logType>PERFORM_ACTION</logType> 
     <state reference="../../../definition/states/pl.net.bluesoft.rnd.processtool.model.config.ProcessStateConfiguration[4]"/> 
     <processInstance reference="../../.."/> 
     <user reference="../../../creator"/> 
    </pl.net.bluesoft.rnd.processtool.model.ProcessInstanceLog> 

Nel mio caso, l'attributo di classe non era importante, quindi ho ignorato il suo valore. Puoi ovviamente armeggiare con esso.

+0

+1 Molto utile, grazie! Il mio prossimo problema è un PersistentSet avvolto in un UnmodifiableSet che, fastidiosamente, non è una classe visibile: -/ – Rup

+0

Per sopprimere l'attributo di classe penso che sia necessario implementare un Mapper personalizzato che ritorni serializedClass (PersistentSet.class) == HashSet .classe. Ma sembra che sia una buona quantità di lavoro da impostare. – Rup

1

Non ho usato, ma xstream-for-beans sembra adattarsi (citando):

Questo progetto prevede l'attuazione di mapper e convertitori che migliorano xstream sul seguenti aspetti:

  1. oggetti Serializzazione come sono esposti da getter e setter. Le funzionalità XStream disponibili per i campi devono funzionare per le proprietà definite per le proprietà getter/setter.
  2. Sanitizza la serializzazione degli oggetti gestiti: omette automaticamente i campi non pertinenti e le informazioni sulla classe .
  3. Gestire i campi "offline" e gli oggetti proxy.

ho scritto una volta personalizzati xstream Converter s per affrontare questo problema, come parte di un progetto closed-source, purtroppo. xstream-for-beans si occupa degli stessi problemi, ne vale la pena.

Ho utilizzato l'utilità di Terracotta Pojoizer con successo in passato, ma non credo che sia stato mantenuto.