2012-05-14 11 views
6

Sto provando a convertire un ResultSet in un file XML. Per prima cosa ho usato questo esempio per la serializzazione.Impostazione degli spazi dei nomi e dei prefissi in un documento Java DOM

import org.w3c.dom.bootstrap.DOMImplementationRegistry; 
import org.w3c.dom.Document; 
import org.w3c.dom.ls.DOMImplementationLS; 
import org.w3c.dom.ls.LSSerializer; 

... 

DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); 

DOMImplementationLS impl = 
    (DOMImplementationLS)registry.getDOMImplementation("LS"); 

...  

LSSerializer writer = impl.createLSSerializer(); 
String str = writer.writeToString(document); 

Dopo aver eseguito questo lavoro, ho provato a convalidare il mio file XML, c'erano un paio di avvertimenti. Uno su non avere un doctype. Così ho provato un altro modo per implementarlo. Mi sono imbattuto nella classe Transformer. Questa classe mi consente di impostare la codifica, il doctype, ecc.

L'implementazione precedente supporta la correzione automatica dello spazio dei nomi. Quanto segue non lo fa.

private static Document toDocument(ResultSet rs) throws Exception { 
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
    factory.setNamespaceAware(true); 
    DocumentBuilder builder = factory.newDocumentBuilder(); 
    Document doc = builder.newDocument(); 

    URL namespaceURL = new URL("http://www.w3.org/2001/XMLSchema-instance"); 
    String namespace = "xmlns:xsi="+namespaceURL.toString(); 

    Element messages = doc.createElementNS(namespace, "messages"); 
    doc.appendChild(messages); 

    ResultSetMetaData rsmd = rs.getMetaData(); 
    int colCount = rsmd.getColumnCount(); 

    String attributeValue = "true"; 
    String attribute = "xsi:nil"; 

    rs.beforeFirst(); 

    while(rs.next()) { 
     amountOfRecords = 0; 
     Element message = doc.createElement("message"); 
     messages.appendChild(message); 

     for(int i = 1; i <= colCount; i++) { 

      Object value = rs.getObject(i); 
      String columnName = rsmd.getColumnName(i); 

      Element messageNode = doc.createElement(columnName); 

      if(value != null) { 
       messageNode.appendChild(doc.createTextNode(value.toString())); 
      } else { 
       messageNode.setAttribute(attribute, attributeValue); 
      } 
      message.appendChild(messageNode); 
     } 
     amountOfRecords++; 
    } 
    logger.info("Amount of records archived: " + amountOfRecords); 

    TransformerFactory tff = TransformerFactory.newInstance(); 
    Transformer tf = tff.newTransformer(); 
    tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 
    tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 
    tf.setOutputProperty(OutputKeys.INDENT, "yes"); 

    BufferedWriter bf = createFile(); 
    StreamResult sr = new StreamResult(bf); 
    DOMSource source = new DOMSource(doc); 
    tf.transform(source, sr); 

    return doc; 
} 

Mentre stavo testando l'attuazione precedente ho ottenuto un TransformationException: Namespace per il prefisso 'XSI' non era stata dichiarata. Come puoi vedere, ho provato ad aggiungere uno spazio dei nomi con il prefisso xsi all'elemento root del mio documento. Dopo aver provato questo ho ancora ottenuto l'eccezione. Qual è il modo corretto per impostare gli spazi dei nomi e i loro prefissi?

Modifica: un altro problema che ho con la prima implementazione è che l'ultimo elemento nel documento XML non ha gli ultimi tre tag di chiusura.

risposta

4

Non è stata aggiunta la dichiarazione dello spazio dei nomi nel nodo radice; hai appena dichiarato il nodo radice nello spazio dei nomi, due cose completamente diverse. Quando si crea un DOM, è necessario fare riferimento allo spazio dei nomi su ogni nodo rilevante. In altre parole, quando aggiungi il tuo attributo, devi definirne lo spazio dei nomi (ad es. SetAttributeNS).

Nota a margine: sebbene gli spazi dei nomi XML abbiano l'aspetto di URL, in realtà non lo sono. Non è necessario utilizzare la classe URL qui.

+1

Grazie, funziona ora. Ho imparato qualcosa di nuovo oggi, proprio come ogni giorno. – TrashCan

27

Il modo corretto per impostare un nodo su un documento namespaceAware è quello di utilizzare:

rootNode.createElementNS("http://example/namespace", "PREFIX:aNodeName"); 

in modo da poter sostituire "prefisso" con il proprio prefisso personalizzato e sostituire "aNodeName" con il nome del nodo. Per evitare di dover ogni nodo con la propria dichiarazione di spazio dei nomi è possibile definire gli spazi dei nomi come attributi sul nodo principale in questo modo:

rootNode.setAttribute("xmlns:PREFIX", "http://example/namespace"); 

Si prega di essere sicuri di impostare:

documentBuilderFactory.setNamespaceAware(true) 

In caso contrario non si dispone di namespaceAwareness.

+0

+1, anche se non ho bisogno di chiamare 'setNamespaceAware'. La documentazione per quella funzione suggerisce che è correlata all'analisi. –

+1

Si noti che esiste un metodo 'setPrefix (prefix);' che consente di impostare dinamicamente il prefisso. –

5

Si noti che l'impostazione di un prefisso xmlns con setAttribute è errata. Se si desidera, ad esempio, firmare il DOM, è necessario utilizzare setAttributeNS: element.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:PREFIX", "http://example/namespace");

+0

Qualche esempio specifico in cui hai notato una differenza? Quando trasformo un DOM in un file, utilizzando setAttribute o setAttributeNS si ottiene lo stesso risultato. Hai notato delle differenze nel DOM di runtime che la trasformazione corregge? – JBert