Преобразование и добавление узла Dom

Я использую функцию, которая принимает два элемента DOM - родительский и дочерний из разных документов. Я импортирую дочерний элемент, преобразовываю его, а затем добавляю к родительскому элементу. Но последняя строка в следующем коде вызывает исключение dom: org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: узел используется в документе, отличном от того, который его создал.

Пожалуйста, смотрите мой код ниже:

    public void attachNodeToParent (Element parent, Element child) throws Exception {
        Document parent_doc = parent.getOwnerDocument();
        child = (Element)parent_doc.importNode(child, true);
// Imported child Element is shown below:
//      <node id="101">
//        <node id="102">
//          <node id="103" />
//        </node>
//        <node id="104">
//          <node id="103" />
//        </node>
//      </node>

        // convert child Element into String
        Source source = new DOMSource(child);
        StringWriter stringWriter = new StringWriter();
        Result result = new StreamResult(stringWriter);
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer();
        transformer.transform(source, result);
        String childXml = stringWriter.getBuffer().toString();


        // Recursively modify the id attributes of every node
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(new InputSource(new StringReader(childXml)));
        XPathFactory xpathFactory = XPathFactory.newInstance();
        XPath xpath = xpathFactory.newXPath();
        NodeList nodes = (NodeList) xpath.compile("//node[@id]").evaluate(doc, XPathConstants.NODESET);
        for (int nodeNumber = 0; nodeNumber < nodes.getLength(); ++nodeNumber) {
            final Element node = (Element) nodes.item(nodeNumber);
            final String nodeId = node.getAttribute("id");
            final String newNodeId = "prefix/" + nodeId;
            node.getAttributeNode("id").setValue(newNodeId);
        }


        final StringWriter writer = new StringWriter();
        transformer.transform(source, new StreamResult(writer));
        writer.flush();
        writer.close();
        String transformedChildXml = writer.toString();

        // Prase transformedChildXml String into XML
        dbf.setNamespaceAware(true);
        DocumentBuilder builder = dbf.newDocumentBuilder();
        Document document = builder.parse(new InputSource(new StringReader(transformedChildXml)));
        document.setXmlStandalone(false);
        child = document.getDocumentElement();

        // child Element is now transformed to:
//        <node id="prefix/101">
//          <node id="prefix/102">
//            <node id="prefix/103" />
//          </node>
//          <node id="prefix/104">
//            <node id="prefix/103" />
//          </node>
//        </node>

        // append transformed child Element to parent Element
        // Throws o rg.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: 
        // A node is used in a different document than the one that created it.
        parent.appendChild(child); 
    }

person sony    schedule 26.01.2012    source источник


Ответы (1)


Краткий ответ заключается в том, что дочерний элемент в предпоследней строке принадлежит документу, созданному строкой

Document document = builder.parse(new InputSource(new StringReader(transformedChildXml)));

а не документ владельца родителя. Таким образом, вам придется снова использовать importNode, чтобы перенести его в целевой документ.

Но не делай этого. В вашем коде есть два цикла сериализации в String и обратно в цикл Document, что очень неэффективно и ни в чем не нуждается. После того, как вы вызвали importNode в начале, просто исправьте идентификаторы на месте и добавьте дочерний элемент к родительскому в конце.

person Alohci    schedule 27.01.2012
comment
После первоначального импорта узла я не уверен, как рекурсивно преобразовать идентификаторы. Если узел имеет несколько подуровней (в моем примере 2 подуровня), нужно ли рекурсивно проходить через каждый уровень? - person sony; 27.01.2012
comment
Да, вы можете пройти по дереву DOM с помощью рекурсии или получить доступ к набору узлов с идентификаторами так же, как вы сейчас делаете с помощью xpath. Просто измените первый параметр evaluate на child, а xpath на ".//node[@id]". - person Alohci; 27.01.2012
comment
xpath .//node[@id] целевого элемента возвращает NodeList только дочерних узлов. Скажем, целью является ‹node id=101›‹node id=102›‹node id=103 /›‹/node›‹/node›. Тогда только узлы с атрибутами id 102 и 103, но узел с id=101, т.е. корень самого целевого элемента не возвращается. Итак, я сначала изменяю корень цели, а затем изменяю все дочерние узлы. Это выглядит не очень красиво. Вот почему я проанализировал туда и обратно от xml до String. - person sony; 27.01.2012
comment
Верно. Вы можете использовать потомок или себя::*[@id] xpath. - person Alohci; 27.01.2012