2013-07-18 10 views
6

Sto scrivendo un programma Java che legge un file XML, rende alcune modifiche, e scrive di nuovo il codice XML.libreria XML Java che assicuri l'ordine attributo

Usando Java XML DOM API standard, l'ordine degli attributi non è conservato. che è, se ho un file di input come ad esempio:

<person first_name="john" last_name="lederrey"/> 

potrei ottenere un file di output come:

<person last_name="lederrey" first_name="john"/> 

Questo è corretto, perché la specifica XML dice che l'attributo ordine non è significativo.

Tuttavia, il mio programma deve mantenere l'ordine degli attributi, in modo che una persona possa facilmente confrontare il documento di input e di output con uno strumento diff.

Una soluzione per vale a elaborare il documento con SAX (anziché DOM): Order of XML attributes after DOM processing

tuttavia, questo non funziona per il mio caso, perché la trasformazione devo fare un nodo potrebbe dipendere su un'espressione XPATH sull'intero documento. quindi, la cosa più semplice sarebbe avere una libreria xml molto simile alla libreria standard Java java, con l'eccezione che l'ordine degli attributi preservers.

Esiste una tale biblioteca?

ps: per favore, evitare di discutere se devo mantenere l'ordine attributo o meno. questa è una discussione molto interessante, ma non è il punto di questa domanda.

+0

È DOM che ti dà ordine di attributo casuale se aggiungi l'input nello stesso modo (ad eccezione dei valori ovviamente)? Potrebbe non darti l'ordine che desideri, ma sarebbe strano se ti desse un ordine casuale. Voglio dire che l'ordine potrebbe non essere specificato, ma ci sarà un po 'di logica in esso ... –

+0

Penso che probabilmente non ci sia una libreria che ti indichi l'ordine degli attributi nel tuo file XML di origine. Tuttavia, potresti prendere in considerazione il controllo dell'output, in modo da poter creare gli attributi scritti sempre nello stesso ordine definito (ad esempio ordinato per nome). – obecker

+0

@obecker, non ho il controllo sul file xml di input, quindi l'ordine degli attributi sull'input xml è sconosciuto e non posso forzare un ordine. –

risposta

-1

Non si può utilizzare DOM, ma è possibile utilizzare SAX, o interrogare i bambini che utilizzano XPATH Visita questa risposta https://stackoverflow.com/a/3728241/2598693

+0

OP ha già detto che questa risposta non funziona nel suo caso. – mzjn

1

farlo due volte:

leggere il documento utilizzando un parser DOM in modo da avere riferimento, un repository, se vuoi.

poi leggerlo di nuovo con SAX. Nel punto in cui è necessario effettuare la trasformazione, fare riferimento alla versione DOM per determinare ciò che è necessario, quindi emettere ciò che è necessario nel mezzo dello streaming SAX.

+0

che sarebbe un'opzione, ma solo decidere se devo aggiornare o meno il nodo corrente sarebbe un dolore nel culo (ho bisogno di modificare un elemento solo se la sua genitore ha tre bambini con uno specifico contenuto, e una delle i child potrebbero essere dopo il nodo corrente). tuttavia, questo approccio sarebbe ok se c'è un modo per navigare dalla posizione corrente nel SAX al suo nodo DOM corrispondente. (ad esempio, quando il sax mi dice che avvia un nuovo nodo, dovrei essere in grado di avere un'espressione xpath che posso applicare alla dom e ottenere il nodo corrispondente). esiste una funzione di supporto già implementata? –

+0

nota, tuttavia, che penso che sarebbe molto più facile se scoprissimo una libreria simile al DOM java standard che conserva solo l'attributo dell'ordine. –

0

La cosa migliore sarebbe quella di utilizzare StAX invece di DOM per la generazione del documento originale. StAX ti offre un controllo eccellente su queste cose e ti consente di trasmettere in streaming progressivamente a un flusso di output invece di tenerlo tutto in memoria.

1

una risposta a coloro che sono arrivati ​​in ritardo alla festa: Saxon in questi giorni offre un'opzione di serializzazione [1] per controllare l'ordine in cui gli attributi vengono emessi. Non mantiene l'ordine di input (perché Saxon non conosce l'ordine di input) ma consente di controllare, ad esempio, che l'attributo ID venga sempre visualizzato per primo. E questo può essere molto utile se l'XML verrà modificato manualmente; L'XML in cui gli attributi appaiono nell'ordine "sbagliato" può essere molto disorientante per un lettore o un editore umano.

Se si utilizza questo come parte di un processo di diffusione, si consiglia di inserire entrambi i file in un processo che normalizza l'ordine degli attributi prima di confrontarli. Tuttavia, per confrontare i file il mio approccio preferito è analizzarli entrambi e utilizzare la funzione XPath deep-equal(); o per utilizzare uno strumento specializzato come DeltaXML.

[1] saxon: attributo-order - vedere http://www.saxonica.com/documentation/index.html#!extensions/output-extras/serialization-parameters

0

Abbiamo avuto esigenze simili a descrizione di Dave. Una soluzione che funzionava era basata sulla riflessione Java.

L'idea è di impostare il propOrder per gli attributi in fase di esecuzione. Nel nostro caso c'è l'elemento APP_DATA che contiene 3 attributi: app, chiave, valore. La classe AppData generato include "contenuto" in propOrder e nessuno degli altri attributi:

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlType(name = "AppData", propOrder = { 
    "content" 
}) 
public class AppData { 

    @XmlValue 
    protected String content; 
    @XmlAttribute(name = "Value", required = true) 
    protected String value; 
    @XmlAttribute(name = "Name", required = true) 
    protected String name; 
    @XmlAttribute(name = "App", required = true) 
    protected String app; 
    ... 
} 

Così Java riflessione è stato utilizzato come segue per impostare l'ordine in fase di esecuzione:

 final String[] propOrder = { "app", "name", "value" }; 
     ReflectionUtil.changeAnnotationValue(
       AppData.class.getAnnotation(XmlType.class), 
       "propOrder", propOrder); 

     final JAXBContext jaxbContext = JAXBContext 
       .newInstance(ADI.class); 
     final Marshaller adimarshaller = jaxbContext.createMarshaller(); 
     adimarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, 
       true); 

     adimarshaller.marshal(new JAXBElement<ADI>(new QName("ADI"), 
       ADI.class, adi), new StreamResult(fileOutputStream)); 

Il changeAnnotationValue() era preso in prestito da questo post: Modify a class definition's annotation string parameter at runtime

Ecco il metodo per la vostra convenienza (il merito va a @assylias e @Balder):

/** 
* Changes the annotation value for the given key of the given annotation to newValue and returns 
* the previous value. 
*/ 
@SuppressWarnings("unchecked") 
public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue){ 
    Object handler = Proxy.getInvocationHandler(annotation); 
    Field f; 
    try { 
     f = handler.getClass().getDeclaredField("memberValues"); 
    } catch (NoSuchFieldException | SecurityException e) { 
     throw new IllegalStateException(e); 
    } 
    f.setAccessible(true); 
    Map<String, Object> memberValues; 
    try { 
     memberValues = (Map<String, Object>) f.get(handler); 
    } catch (IllegalArgumentException | IllegalAccessException e) { 
     throw new IllegalStateException(e); 
    } 
    Object oldValue = memberValues.get(key); 
    if (oldValue == null || oldValue.getClass() != newValue.getClass()) { 
     throw new IllegalArgumentException(); 
    } 
    memberValues.put(key,newValue); 
    return oldValue; 
} 

Speriamo che questo aiuti qualcuno!

Problemi correlati