2012-05-14 10 views
6

Estoy tratando de convertir un ResultSet a un archivo XML. Primero utilicé este ejemplo para la serialización.Configuración de espacios de nombres y prefijos en un documento DOM de Java

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

Después de hacer este trabajo, traté de validar mi archivo XML, hubo un par de advertencias. Uno sobre no tener un doctype. Así que probé otra forma de implementar esto. Encontré la clase Transformer. Esta clase me permite establecer la codificación, doctype, etc.

La implementación anterior admite la corrección automática del espacio de nombres. Lo siguiente no.

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

Mientras estaba probando la aplicación anterior recibí un TransformationException: Espacio de nombres para el prefijo 'xsi' no se ha declarado. Como puede ver, he intentado agregar un espacio de nombres con el prefijo xsi al elemento raíz de mi documento. Después de probar esto, todavía tengo la excepción. ¿Cuál es la forma correcta de establecer espacios de nombres y sus prefijos?

Editar: Otro problema que tengo con la primera implementación es que el último elemento en el documento XML no tiene las últimas tres etiquetas de cierre.

Respuesta

4

No ha agregado la declaración del espacio de nombres en el nodo raíz; usted acaba de declarar el nodo raíz en el espacio de nombres, dos cosas completamente diferentes. Al construir un DOM, necesita hacer referencia al espacio de nombres en cada nodo relevante. En otras palabras, cuando agrega su atributo, necesita definir su espacio de nombre (por ejemplo, setAttributeNS).

Nota al margen: aunque los espacios de nombres XML se parecen a las URL, realmente no lo son. No es necesario usar la clase de URL aquí.

+1

Gracias, está funcionando ahora. Aprendí algo nuevo hoy, como todos los días. – TrashCan

27

La forma correcta de establecer un nodo en un documento namespaceAware es mediante el uso de:

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

para que pueda reemplazar "prefijo" con su propio prefijo personalizado y reemplazar "aNodeName" con el nombre de su nodo. Para evitar que cada nodo tiene su propia declaración de espacio se pueden definir los espacios de nombres como atributos en el nodo raíz de este modo:

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

Por favor, no olvide ajustar:

documentBuilderFactory.setNamespaceAware(true) 

De lo contrario, no tiene namespaceAwareness.

+0

+1, aunque no necesité llamar a 'setNamespaceAware'. La documentación para esa función sugiere que está relacionado con el análisis sintáctico. –

+1

Tenga en cuenta que hay un método 'setPrefix (prefix);' que permite establecer dinámicamente el prefijo. –

5

Tenga en cuenta que establecer un prefijo xmlns con setAttribute es incorrecto. Si alguna vez quiere, por ejemplo, firmar su DOM, tiene que usar setAttributeNS: element.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:PREFIX", "http://example/namespace");

+0

¿Algún ejemplo específico en el que notó una diferencia? Cuando transformo un DOM en un archivo, usar setAttribute o setAttributeNS da el mismo resultado. ¿Notó alguna diferencia en el DOM de tiempo de ejecución que corrige la transformación? – JBert

Cuestiones relacionadas