2009-08-22 13 views
5

Jak mogę, biorąc pod uwagę DOM DOM DOMU W3C (konkretnie domyślna implementacja Java) zmienić przestrzeń nazw każdego elementu/atrybutu/węzła w tym DOM? Skutecznie, najlepiej. DOM nie wydaje się mieć na to metody setNamespaceURI, co jest niewygodne.Jak mogę zmienić obszar nazw w każdym węźle w DOM?

Próbowałem podejść XSL, ale nie udało im się pracować w transformatorach JAXP (chociaż działają poprawnie w Saxon9B, którego nie mogę użyć z innych powodów).

Zasadniczo potrzebuję czystego jądra rozwiązania java, które pozwoli mi wziąć jeden dokument i zmienić jego przestrzeń nazw.

+0

XSL jest chyba najprostszym rozwiązaniem i powinien działać w JAXP. Czego próbowałeś i jak mu się nie udało? – skaffman

Odpowiedz

3

Na podstawie mojej wielce tendencyjnej opinii, czego chcesz, będzie ogromny ból w dupę. Widzę piekło z typowaniem i liczne rekursywne pętle potrzebne do tego już teraz! Ahh, domyślna implementacja Java, jak nienawidzę twoich NPE: s w internals, odwrócona logika, dodatkowe kroki potrzebne do prostych operacji!

Tak, moja sugestia byłaby rekurencyjna dla pętli z typowaniem dla każdego możliwego typu węzła, na podstawie mojego bardzo osobistego doświadczenia, że ​​domyślna implementacja Javy jest do tego sarkała.

+0

Myślę, że niektóre z innych rozwiązań są tutaj bardziej eleganckie, ale tak właśnie zrobiłem :) –

+0

Chociaż trochę zabawne, to naprawdę nie powinno być akceptowaną odpowiedzią ... –

9

To nie działa w DOM-u świadomym przestrzeni nazw. Będziesz musiał użyć DOM Level 3 Core method Document.renameNode (javadoc) na każdym podrzędnym elemencie, którego przestrzeń nazw chcesz zmienić. (Zazwyczaj nie trzeba zmieniać tak wielu węzłów Attr, ponieważ przestrzeń nazw węzła Attr bez prefiksu jest zawsze pusta, zamiast przestrzeni nazw Element.)

Jeśli wszystko, co chcesz zrobić, to zastąpić jedną przestrzeń nazw dla inny, może być szybszy w użyciu DOM-a bez przestrzeni nazw i po prostu zmienić odpowiedni atrybut xmlns. Powinieneś być w stanie uzyskać DOM pozbawiony przestrzeni nazw, ustawiając parametr 'namespaces' na wartość DOMConfiguration na false, ale nie próbowałem tego w Javie i jest to trochę niejasna rzecz, której DOM imps mógłby się pomylić.

2

Jeśli chcesz zmienić tylko przestrzeń nazw, użyj edytora strumienia, aby zmienić odwzorowanie NS na URL.

Namspace to mniej więcej powiązanie między prefiksem przestrzeni nazw a identyfikatorem URI. W celu szybkiej zmiany nazw, wystarczy zmienić mapowanie:

Przed: xmlns: myns = "my-namespace-uri"

Po: xmlns: myns = "my-new-namespace-uri"

Zasadniczo zmiana odwzorowania jest wystarczająca, jeśli celem jest po prostu zmiana obszaru nazw. Co więcej, jeśli Dokument XML ma domyślny obszar nazw, zmiana domyślnej wartości adresu URL przestrzeni nazw zmieniłaby przestrzeń nazw dla całego dokumentu.

Przed: xmlns = "my-namespace-uri"

Po: xmlns = "my-new-namespace-uri"

+0

+1 dla czystej implementacji Java –

+0

Nice pomysł, ale w tym momencie wymagałoby to ponownej serializacji do strumienia bajtów i ponownego przetworzenia, co nie jest dopuszczalne z punktu widzenia wydajności. Mam już DOM, z którego muszę korzystać. –

0

Przestrzeń nazw zmienia się na każdym elemencie bez określonej przestrzeni nazw prefiksu poprzez zastosowanie atrybutu targetnamespace do elementu głównego. Wykonanie tej czynności będzie również wymagało zmiany każdego z elementów przedrostkiem przestrzeni nazw. Możesz zmienić ten prefiks ręcznie lub napisać logikę skryptu, aby przejść drzewo DOM do stosowania go tylko w razie potrzeby.

Oto więcej czytać o atrybucie targetNamespace i atrybutu 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

Jak mogę, zważywszy W3C DOM (Domyślna implementacja Javy, a konkretnie) zmiany nazw każdego element/atrybut/węzeł w tym DOM? Skutecznie, najlepiej.

Nie sądzę, że istnieje skuteczne rozwiązanie, które jest również solidne. Nie możesz zmienić nazwy czegoś na głównym elemencie. Następujące dokumenty:

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> 

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

Te trzy dokumenty są równoważne w przestrzeni nazw świadomy DOM. Możesz uruchomić ten sam namespaced XPath queries przeciwko którymkolwiek z nich.

Ponieważ DOM pozwala określić dokładnie, jak węzły powinny być nazwane, nie ma żadnego catch-all, jednoetapowe wywołanie, aby zmienić obszar nazw. Musisz chodzić DOM, biorąc pod uwagę nie tylko wartości prefiksów i URI, ale ich scope w dowolnym momencie.

XSLT ta może być używana z elementami do zmiany Transformer przestrzeni nazw jak urn:fleet do przestrzeni nazw jak 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> 

Ostrzeżenia: dalsze szczypanie byłyby wymagane do obsługi przestrzeni nazw cech; zwisające deklaracje mogą pozostać w tyle, co jest nieporządne, ale w dużej mierze nieistotne; prawdopodobnie inne rzeczy, o których nie myślałem.

+0

To było to, co pierwotnie użyłem, prawie słowo w słowo, ale okazuje się, że używanie XML przez naszego klienta jest ... prymitywne. Albo głupi. Wybierz. Ich narzędzia łamią się, jeśli XML nie zostanie poprawnie "wyciągnięty", na litość boską. –

0

Możesz skopiować drzewo DOM do innego drzewa i wprowadzić pewne poprawki podczas procesu. Na przykład, używając org.apache.xml.utils.DOMBuilder jak wdrożenie ContentHandler, można przesłonić metody w taki sposób:

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

DOMBuilder będzie obsługiwać wszystkie brudną robotę podczas kopiowania pozostawiając Państwu tylko logiki zastąpienie nazw .

0

Jeśli są ok, z wykorzystaniem klas XERCES, można utworzyć DOMParser który zastępuje URI atrybutów i elementów ze swoimi stałymi się URI:

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

gdzie indziej, można analizować do dokumentu DOM :

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

Ten kod podany DOM Document powróci nowy DOM dokumentu, w którym zostały zastosowane dany zestaw nazw URI tłumaczeń (uriMap). Kluczami muszą być identyfikatory URI w dokumencie źródłowym, wartości zastępczych identyfikatorów URI w dokumencie docelowym. Nieznane identyfikatory URI przestrzeni nazw przechodzą przez niezmienione.Wie, aby zmienić wartość xmlns: * atrybuty, ale nie zmieni inne atrybuty, które mogą się zdarzyć mieć URI przestrzeni nazw jako ich wartości (np 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; 
} 
Powiązane problemy