Как я могу изменить пространство имен на каждом узле в DOM? - PullRequest
5 голосов
/ 22 августа 2009

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

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

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

Ответы [ 8 ]

10 голосов
/ 23 августа 2009

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

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

3 голосов
/ 22 августа 2009

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

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

2 голосов
/ 23 августа 2009

Как я могу, с учетом 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 с поддержкой пространства имен. Вы можете выполнять те же запросы XPath в пространстве имен для любого из них.

Поскольку 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>

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

2 голосов
/ 23 августа 2009

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

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

Перед тем: Xmlns: myNS = "мои-имена-URI"

После того, как: Xmlns: myNS = "мой новый-имен-URI"

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

До: XMLNS = "мои-имена-URI"

После того, как: XMLNS = "мой новый-имен-URI"

1 голос
/ 22 мая 2013

Этот код, заданный для документа DOM, вернет новый документ DOM, в котором был применен заданный набор преобразований 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;
}
0 голосов
/ 13 февраля 2013

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

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

DOMParse p = new MyDOMParser(...);
p.parse(new InputSource(inputStream));
Document doc = p.getDocument();
0 голосов
/ 17 ноября 2009

Вы можете скопировать свое дерево 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 голосов
/ 23 августа 2009

Пространство имен изменяется для каждого элемента без определенного префикса пространства имен, применяя атрибут 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...