2009-08-22 11 views
5

¿Cómo puedo, dado un DOM W3C (aplicación por defecto de Java, específicamente) cambiar el espacio de nombres de cada elemento/atributo/nodo en que DOM? Eficientemente, preferiblemente. El DOM no parece tener un método setNamespaceURI, lo cual es inconveniente.¿Cómo puedo cambiar el espacio de nombres en cada nodo en un DOM?

He intentado con enfoques XSL, pero no han funcionado en los transformadores JAXP (aunque funcionan bien en Saxon9B, que no puedo usar por otros motivos).

Básicamente, necesito una solución java de núcleo puro que me permita tomar un documento y cambiar su espacio de nombre.

+0

XSL es probablemente la solución más fácil, y debe trabajar en JAXP. ¿Qué intentaste y cómo falló? – skaffman

Respuesta

3

Basado en mi opinión muy parcial, lo que quieras será un gran dolor en el culo. ¡Puedo ver el infierno de encasillamiento y numerosos bucles recursivos necesarios para hacer esto de manera confiable! ¡Ahh, la implementación predeterminada de Java, cómo odio tu NPE: s en el interior, la lógica invertida, los pasos adicionales necesarios para operaciones simples!

Así que sí, mi sugerencia sería recursiva para los bucles con encasillado para cada tipo de nodo posible, según mi experiencia personal, la implementación predeterminada de Java es muy mala.

+0

Creo que algunas de las otras soluciones aquí son más elegantes, pero esto es lo que terminé haciendo :) –

+0

Aunque es algo gracioso, esta realmente no debería ser la respuesta aceptada ... –

9

Esto no es eficiente en un DOM consciente del espacio de nombres. Usted tendría que utilizar el método 3 Núcleo del DOM Nivel Document.renameNode (javadoc) en cada elemento descendiente cuyo espacio de nombre desea cambiar. (Normalmente no necesitaría cambiar tantos nodos Attr, porque el espacio de nombres de un nodo Attr sin prefijo siempre es nulo, en lugar del espacio de nombres del Elemento.)

Si todo lo que quiere hacer es sustituir un espacio de nombres por otro, podría ser más rápido usar un DOM que no reconoce el espacio de nombres y simplemente cambiar el atributo xmlns en cuestión. Usted debe ser capaz de conseguir un espacio de nombres DOM-conscientes ajustando el parámetro DOMConfiguration los espacios de nombres ‘’ a falso, pero no he probado esto en Java y es la especie de diablillos cosa DOM oscuros sería equivocarse.

2

Si la intención es simplemente cambiar el espacio de nombre, simplemente use algún editor de flujo para cambiar la asignación NS a URL.

Un Namspace es más o menos un enlace entre el prefijo del espacio de nombres y un URI. Con el fin de cambiar rápidamente de espacio de nombres, sólo cambia el mapeo:

Antes: xmlns: myNS = "mi-espacio de nombres-uri"

Después: xmlns: myNS = "mi-nuevo-espacio de nombres-uri"

El mapeo que cambia básicamente es suficiente, si la intención es simplemente cambiar el espacio de nombres. Además, si el documento XML tiene un espacio de nombre predeterminado, entonces al cambiar el valor predeterminado de la URL del espacio de nombres se cambiaría el espacio de nombres para todo el documento.

Antes: xmlns = "mi-espacio de nombres-uri"

Después: xmlns = "mi-new-namespace-uri"

+0

+1 para la implementación pura de Java –

+0

Nice idea, pero en este punto que requeriría volver a serializar a un flujo de bytes y volver a analizar, que NO es aceptable desde el punto de vista del rendimiento. Ya tengo un DOM, que debo usar. –

0

Se cambia el espacio de nombres en cada elemento sin un prefijo de espacio de nombres definido aplicando un atributo de espacio de nombre de destino a su elemento raíz. Hacer esto también requerirá que luego modifiques cada uno de tus elementos con un prefijo de espacio de nombres. Puede hacer que este prefijo cambie manualmente o escribir una lógica de guión para hacer que su árbol DOM lo aplique solo cuando sea necesario.

Aquí es más lectura sobre el atributo targetNamespace y el atributo nonamespaceschema:

http://www.xml.com/pub/a/2000/11/29/schemas/part1.html?page=8 http://www.computerpoweruser.com/editorial/article.asp?article=articles%2Farchive%2Fc0407%2F48c07%2F48c07.asp

2

¿Cómo puedo, dado un DOM W3C (aplicación por defecto de Java, específicamente) cambiar el espacio de nombres de cada elemento/atributo/nodo en ese DOM? Eficientemente, preferiblemente.

No creo que haya una solución eficiente que también sea robusta. No puede simplemente cambiar el nombre de algo en el elemento raíz. Tenga en cuenta estos documentos:

Doc1

<?xml version="1.0" encoding="UTF-8"?> 
<root xmlns="urn:all" xmlns:f="urn:fleet" xmlns:m="urn:mission"> 
    <f:starfleet> 
    <m:bold> 
     <f:ship name="Enterprise" /> 
    </m:bold> 
    </f:starfleet> 
</root> 

Doc2

<?xml version="1.0" encoding="UTF-8"?> 
<root xmlns="urn:all"> 
    <starfleet xmlns="urn:fleet"> 
    <bold xmlns="urn:mission"> 
     <ship xmlns="urn:fleet" name="Enterprise" /> 
    </bold> 
    </starfleet> 
</root> 

Doc3

<?xml version="1.0" encoding="UTF-8"?> 
<r:root xmlns:r="urn:all"> 
    <r:starfleet xmlns:r="urn:fleet"> 
    <r:bold xmlns:r="urn:mission"> 
     <r:ship xmlns:r="urn:fleet" name="Enterprise" /> 
    </r:bold> 
    </r:starfleet> 
</r:root> 

Estos tres documentos son equivalentes en un espacio de nombres DOM-conscientes. Puede ejecutar el mismo namespaced XPath queries contra cualquiera de ellos.

Desde el DOM permite especificar exactamente cómo se deben namespaced nodos, no existe un cajón de sastre, llamada de un solo paso para cambiar un espacio de nombres. Debe recorrer el DOM, teniendo en cuenta no solo los valores de prefijo y URI, sino también su scope en un momento determinado.

Este XSLT se puede utilizar con un Transformer para cambiar elementos de espacios de nombres como urn:fleet ser espacio de nombres como urn:new:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:f="urn:fleet" version="1.0"> 
    <xsl:output method="xml" indent="yes" /> 
    <xsl:template match="*"> 
    <xsl:copy> 
     <xsl:copy-of select="@*" /> 
     <xsl:apply-templates /> 
    </xsl:copy> 
    </xsl:template> 
    <xsl:template match="f:*"> 
    <xsl:variable name="var.foo" select="local-name()" /> 
    <xsl:element namespace="urn:new" name="{$var.foo}"> 
     <xsl:copy-of select="@*" /> 
     <xsl:apply-templates /> 
    </xsl:element> 
    </xsl:template> 
</xsl:stylesheet> 

Advertencias: sería necesario ajustar aún más para manejar atributos de espacios de nombres; las declaraciones urn:fleet colgantes pueden dejarse atrás, lo cual es complicado, pero en gran medida no tiene importancia; Probablemente otras cosas en las que no había pensado.

+0

Eso fue lo que usé originalmente, casi palabra por palabra, pero resulta que el uso de XML de nuestro cliente es ... primitivo. O estúpido. Usted escoge. Sus herramientas se rompen si el XML no se elimina adecuadamente, por el amor de Cristo. –

0

Usted puede copiar su árbol DOM a otro árbol y hacer algunos ajustes durante el proceso. Por ejemplo, el uso de org.apache.xml.utils.DOMBuilder como la implementación de ContentHandler, es posible reemplazar los métodos de tal manera:

public void startElement(String ns, String localName, String name, Attributes atts) throws SAXException { 
     super.startElement("new_namespace", localName, name, atts); 
    } 

DOMBuilder se encargará de todo el trabajo sucio durante el copiado a dejar solo la lógica de reemplazo de espacio de nombres .

0

Si estás bien con el uso de las clases Xerces, se puede crear un DOMParser que sustituye a la URI de atributos y elementos con sus URIs fijos hasta:

import org.apache.xerces.parsers.DOMParser; 

public static class MyDOMParser extends DOMParser { 
    private Map<String, String> fixupMap = ...; 

    @Override 
    protected Attr createAttrNode(QName attrQName) 
    { 
     if (fixupMap.containsKey(attrQName.uri)) 
      attrQName.uri = fixupMap.get(attrQName.uri); 
     return super.createAttrNode(attrQName); 
    } 

    @Override 
    protected Element createElementNode(QName qName) 
    { 
     if (fixupMap.containsKey(qName.uri)) 
      qName.uri = fixupMap.get(qName.uri); 
     return super.createElementNode(qName); 
    }  
} 

La otra parte, se puede analizar en un documento DOM :

DOMParse p = new MyDOMParser(...); 
p.parse(new InputSource(inputStream)); 
Document doc = p.getDocument(); 
0

Este código le da un documento DOM devolverá un nuevo documento DOM en el que se han aplicado una serie dada de espacio de nombres traducciones URI (URIMAP). Las claves deben ser los URI en el documento fuente, los valores los URI de reemplazo en el documento de destino. Los URI de espacio de nombre desconocido pasan sin cambios.Se sabe que cambiar el valor de xmlns: * atributos, pero no va a cambiar otros atributos que pueden ocurrirle a tener URI de espacio de nombres como sus valores (por ejemplo XSD targetNamespace)

private static Node makeClone(Node kid, Node to, Map<String, String> uriMap) { 
    Document doc = to.getNodeType() == Node.DOCUMENT_NODE ? 
      (Document) to : 
      to.getOwnerDocument(); 
    if (kid.getNodeType() == Node.ELEMENT_NODE) { 
     String newURI = 
       uriMap.containsKey(kid.getNamespaceURI()) ? 
       uriMap.get(kid.getNamespaceURI()) : 
       kid.getNamespaceURI(); 
     Element clone = doc.createElementNS(newURI, kid.getNodeName()); 
     to.appendChild(clone); 
     for (int i = 0; i < kid.getAttributes().getLength(); i++) { 
     Attr attr = (Attr) kid.getAttributes().item(i); 
     String newAttrURI = 
       uriMap.containsKey(attr.getNamespaceURI()) ? 
       uriMap.get(attr.getNamespaceURI()) : 
       attr.getNamespaceURI(); 
     String newValue = attr.getValue(); 
     if (attr.getNamespaceURI() != null && 
       attr.getNamespaceURI().equals(
       "http://www.w3.org/2000/xmlns/") && 
       uriMap.containsKey(attr.getValue())) 
      newValue = uriMap.get(attr.getValue()); 
     clone.setAttributeNS(newAttrURI, attr.getNodeName(), newValue); 
     } 
     return clone; 
    } 
    Node clone = kid.cloneNode(false); 
    doc.adoptNode(clone); 
    to.appendChild(clone); 
    return clone; 
} 

private static void copyKidsChangingNS(Node from, Node to, 
     Map<String, String> uriMap) { 
    NodeList kids = from.getChildNodes(); 
    for (int i = 0; i < kids.getLength(); i++) { 
     Node kid = kids.item(i); 
     Node clone = makeClone(kid, to, uriMap); 
     copyKidsChangingNS(kid, clone, uriMap); 
    } 
} 

public static Document changeDocNS(Document doc, Map<String, String> uriMap) 
     throws Exception { 
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
    dbf.setNamespaceAware(true); 
    DocumentBuilder db = dbf.newDocumentBuilder(); 
    Document newDoc = db.newDocument(); 
    copyKidsChangingNS(doc, newDoc, uriMap); 
    return newDoc; 
} 
Cuestiones relacionadas