2009-08-22 2 views
5

Как я могу, учитывая w3c DOM (реализация по умолчанию Java, в частности) изменить пространство имен каждого элемента/атрибута/узла в этом DOM? Эффективно, предпочтительно. DOM, похоже, не имеет на нем метода setNamespaceURI, что неудобно.Как изменить пространство имен на каждом узле в DOM?

Я пробовал подходы XSL, но они не работали в трансформаторах JAXP (хотя они работают в Saxon9B, что я не могу использовать по другим причинам).

В принципе, мне нужно чистое ядровое Java-решение, которое позволит мне взять один документ и изменить его пространство имен.

+0

XSL является, вероятно, самым простым решением, и должен работать в JAXP. Что вы пробовали, и как это случилось? – skaffman

ответ

3

Основываясь на моем чрезвычайно предвзятом мнении, что вы хотите, будет огромная боль в заднице. Я могу видеть чертовски чертовски и много рекурсивных циклов, чтобы сделать это надежно уже! Ahh, реализация Java по умолчанию, как я ненавижу ваш NPE: s на внутренностях, обратная логика, дополнительные шаги, необходимые для простых операций!

Так что да, мое предложение было бы рекурсивным для циклов с typecasting для каждого единственного возможного типа узла, на основе моего личного опыта реализация Java по умолчанию отстойла это плохо.

+0

Я думаю, что некоторые из других решений здесь более элегантные, но это то, что я закончил делать :) –

+0

Хотя несколько смешно, это действительно не должно быть принятым ответом ... –

9

Это неэффективно для DOM с пространством имен. Вам нужно будет использовать метод DOM Level 3 Core Document.renameNode (javadoc) на каждом потолочном элементе, пространство имен которого вы хотели бы изменить. (Обычно вам не нужно было изменять так много узлов Attr, потому что пространство имен узла Attr без префикса всегда равно null, а не пространство имен Element.)

Если все, что вы хотите сделать, это заменить одно пространство имен для другой, возможно, быстрее использовать пространство имен, не знающее DOM, и просто изменить рассматриваемый атрибут xmlns. Вы должны иметь возможность получить DOM с пространством имен, установив параметр DOMConfiguration «namespaces» в значение false, но я не пробовал это на Java, и это какая-то неясная вещь, которую DOM imps ошибается.

2

Если целью является просто изменить пространство имен, то просто используйте редактор потоков, чтобы изменить сопоставление NS на URL.

Namspace более или менее является связующим звеном между префиксом пространства имен и URI. Для того, чтобы быстро изменить пространство имен, просто изменить отображение:

До: Xmlns: myNS = "мой-имена-URI"

После: XMLNS: myNS = "мои новые-имена-URI"

Достаточно существенно изменить отображение, если целью является просто изменить пространство имен. Более того, если XML Document имеет пространство имен по умолчанию, то изменение значения URL-адреса пространства имен по умолчанию изменит пространство имен для всего документа.

До: Xmlns = "мой-имен-URI"

После: Xmlns = "мой новый-пространство имен URI"

+0

+1 для чистой реализации Java –

+0

Nice идея, но на этом этапе потребуется повторная сериализация потока байтов и повторного разбора, что НЕ приемлемо с точки зрения производительности. У меня уже есть DOM, который я должен использовать. –

0

Пространство имен изменяется на каждом элементе без определенного префикса пространства имен путем применения атрибута targetnamespace к вашему корневому элементу. Для этого также потребуется, чтобы вы затем изменяли каждый из ваших элементов с помощью префикса пространства имен. Вы можете изменить этот префикс вручную или написать некоторую логику сценария, чтобы пройти дерево DOM, применяя его только там, где это необходимо.

Здесь больше читать об атрибуте TargetNamespace и атрибут 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

Как я могу, учитывая w3c DOM (реализация Java по умолчанию, в частности) изменить пространство имен каждый элемент/атрибут/узел в этом DOM? Эффективно, предпочтительно.

Я не думаю, что есть эффективное решение, которое также является надежным. Вы не можете просто переименовать что-то в корневой элемент. Рассмотрим эти документы:

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> 

Эти три документа эквивалентны в пространств имён DOM. Вы можете запустить тот же namespaced XPath queries против любого из них.

Поскольку DOM позволяет вам точно указать, как узлы должны быть помещены в пространство имен, не существует единого одноразового вызова для изменения пространства имен. Вам нужно пройти DOM с учетом не только префикса и значений URI, но и их scope в любой момент времени.

Этот XSLT может быть использован с Transformer, чтобы изменить элементы, как urn:fleet пространств имён, чтобы быть в пространстве имен 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> 

Предостережения: дальнейшее тонкой настройки необходимо будет обрабатывать атрибуты пространств имён; dangling urn:fleet объявления могут быть оставлены позади, что является грязным, но в значительной степени несущественным; вероятно, другие вещи, о которых я не думал.

+0

Это то, что я изначально использовал, почти дословно, но, оказывается, использование нашего клиента XML является ... примитивным. Или глупо. Вы выбираете. Их инструменты ломаются, если XML не выдается правильно, ради Христа. –

0

Вы можете скопировать дерево DOM на другое дерево и внести некоторые изменения во время процесса. Например, используя org.apache.xml.utils.DOMBuilder как осуществление ContentHandler, вы можете переопределить методы таким образом:

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

DOMBuilder будет обрабатывать всю грязную работу во время копирования оставляя вам только замена имен логики ,

0

Если вы нормально с использованием классов Xerces, вы можете создать DomParser, который заменяет URI атрибутов и элементов с фиксированным до 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); 
    }  
} 

в другом месте, вы можете разобрать в DOM-документ :

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

Этот код дан DOM Document возвращает новый DOM Document, в котором был применен данный набор пространств имен URI переводов (uriMap). Ключами должны быть URI в исходном документе, значения заменяемых URI в целевом документе. Неизвестные URI пространства имен проходят без изменений.Он знает, что нужно изменить значение Xmlns: * атрибуты, но не будет изменять другие атрибуты, которые могут произойти, чтобы иметь пространство имен URI, как и их значения (например 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; 
} 
Смежные вопросы