2009-08-22 6 views
5

Comment puis-je, étant donné un DOM w3c (implémentation par défaut de Java, en particulier), changer l'espace de noms de chaque élément/attribut/nœud dans ce DOM? Efficacement, de préférence. Le DOM ne semble pas avoir de méthode setNamespaceURI, ce qui n'est pas pratique. J'ai essayé les approches XSL, mais elles n'ont pas fonctionné dans les transformateurs JAXP (bien qu'elles fonctionnent correctement avec Saxon9B, que je ne peux pas utiliser pour d'autres raisons). Fondamentalement, j'ai besoin d'une solution java de base pure qui me permettra de prendre un document et de changer son espace de noms.Comment est-ce que je peux changer l'espace de noms sur chaque noeud dans un DOM?

+0

XSL est probablement la solution la plus simple, et devrait travailler en JAXP. Qu'avez-vous essayé, et comment cela a-t-il échoué? – skaffman

Répondre

3

Basé sur mon opinion extrêmement biaisée ce que vous voulez sera une énorme douleur dans le cul. Je peux voir l'enfer de typecasting et de nombreuses boucles récursives nécessaires pour le faire de manière fiable déjà! Ahh, l'implémentation par défaut de Java, comment je déteste vos NPE: s à l'interne, la logique inverse, les étapes supplémentaires nécessaires pour des opérations simples! Donc oui, ma suggestion serait récursive pour les boucles avec typecasting pour chaque type de nœud possible, d'après mon expérience très personnelle, l'implémentation par défaut de Java est si mauvaise.

+0

Je pense que certaines des autres solutions ici sont plus élégantes, mais c'est ce que j'ai fini par faire :) –

+0

Bien que quelque peu drôle, cela ne devrait pas être la réponse acceptée ... –

9

Ceci n'est pas efficace sur un DOM qui prend en charge les espaces de noms. Vous devez utiliser la méthode DOM Level 3 Core Document.renameNode (javadoc) sur chaque élément descendant dont vous souhaitez modifier l'espace de noms. (Normalement, vous n'avez pas besoin de modifier autant de nœuds Attr, car l'espace de noms d'un nœud Attr sans préfixe est toujours NULL, plutôt que l'espace de nom de l'élément.)

Si vous souhaitez simplement remplacer un nœud Dans d'autres cas, il peut être plus rapide d'utiliser un DOM sans espace de noms et de simplement modifier l'attribut xmlns en question. Vous devriez pouvoir obtenir un DOM sans espace de noms en définissant le paramètre 'namespaces' DOMConfiguration sur false, mais je n'ai pas essayé cela en Java et c'est le genre de chose obscure que les imps DOM auraient tort.

2

Si l'intention est de simplement changer l'espace de noms, alors utilisez simplement un éditeur de flux pour changer le mappage NS en URL. Un Namspace est plus ou moins une liaison entre le préfixe d'espace de noms et un URI. Pour changer rapidement l'espace de noms, il suffit de changer la mise en correspondance:

Avant: xmlns: monen = "my-namespace-uri"

Après: xmlns: monen = "my-new-namespace-uri"

Le mappage fondamentalement changeant est suffisant, si l'intention est simplement de changer l'espace de noms. De plus, si Document XML a un espace de noms par défaut, la modification de la valeur de l'URL de l'espace de noms par défaut changerait l'espace de noms pour l'ensemble du document.

Avant: xmlns = "my-namespace-uri"

Après: xmlns = "my-new-namespace-uri"

+0

+1 pour l'implémentation Java pure –

+0

Nice idée, mais à ce stade, il faudrait re-sérialiser un flux d'octets et ré-analyser, ce qui n'est pas acceptable du point de vue de la performance. J'ai déjà un DOM, que je dois utiliser. –

0

L'espace de noms est modifié à chaque élément sans préfixe d'espace de nom défini en appliquant un attribut targetnamespace à votre élément racine. Pour ce faire, vous devrez également modifier chacun de vos éléments avec un préfixe d'espace de nom. Vous pouvez modifier ce préfixe manuellement ou écrire une logique de script pour parcourir votre arborescence DOM afin de l'appliquer uniquement si nécessaire.

est ici plus de lecture sur l'attribut targetNamespace et l'attribut 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

Comment puis-je, donné une (implémentation par défaut de Java, en particulier) DOM w3c modifier l'espace de noms de chaque élément/attribut/noeud dans ce DOM? Efficacement, de préférence.

Je ne pense pas qu'il existe une solution efficace qui soit aussi robuste. Vous ne pouvez pas simplement renommer quelque chose sur l'élément racine. Tenez compte de ces documents:

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> 

Ces trois documents sont équivalents dans un DOM espace de nom. Vous pouvez exécuter le même namespaced XPath queries contre l'un d'entre eux.

Étant donné que le DOM vous permet de spécifier exactement comment les nœuds doivent être nommés, il n'y a pas d'appel en une seule étape pour modifier un espace de noms. Vous devez parcourir le DOM, en prenant en compte non seulement les valeurs de préfixe et d'URI, mais leur scope à un moment donné.

Ce XSLT peut être utilisé avec un Transformer de changer les éléments comme urn:fleet à namespaced être comme urn:new espaces de noms:

<?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> 

Défauts: plus peaufinage serait nécessaire pour gérer les attributs d'espace de nom; pendantes urn:fleet déclarations peuvent être laissés pour compte, ce qui est désordonné, mais largement sans conséquence; probablement d'autres choses auxquelles je n'ai pas pensé.

+0

C'est ce que j'ai utilisé à l'origine, presque mot pour mot, mais il s'avère que l'utilisation de XML par nos clients est ... primitive. Ou stupide. Vous choisissez. Leurs outils se brisent si le XML n'est pas correctement mis à jour, pour l'amour du Christ. –

0

Vous pouvez copier votre arborescence DOM dans un autre arbre et effectuer quelques modifications pendant le processus. Par exemple, en utilisant org.apache.xml.utils.DOMBuilder comme la mise en œuvre de ContentHandler, vous pouvez remplacer les méthodes de cette façon:

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

DOMBuilder se chargera tout le travail sale lors de la copie laissant vous seule logique de remplacement de l'espace de noms .

0

Si vous êtes ok avec l'aide des Xerces classes, vous pouvez créer un DOMParser qui remplace l'URI des attributs et des éléments avec vos fixes jusqu'à URIs:

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

L'ailleurs, vous pouvez analyser dans un document DOM :

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

Ce code donné un document DOM renverra un nouveau DOM document dans lequel un ensemble de traductions URI d'espace de noms ont été appliqués (uriMap). Les clés doivent être les URI dans le document source, les valeurs les URI de remplacement dans le document de destination. Les URI d'espace de noms inconnus passent inchangés.Il sait changer la valeur de xmlns: * attributs, mais ne changera pas d'autres attributs qui pourraient se produire d'avoir URIs d'espaces de noms que leurs valeurs (par exemple 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; 
} 
Questions connexes