1

我试图从 JDOM 1.0 更新到 JDOM2。在 JDOM 1.0 中,这段代码:

DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
org.w3c.dom.Document doc = dbFactory.newDocumentBuilder().newDocument();
doc.setXmlVersion("1.0");

Element root = doc.createElement("Document");

root.setAttribute("xmlns", "urn:iso:foo");
root.setAttribute("xsi:schemaLocation", "urn:iso:foo bar.xsd");
root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
doc.appendChild(root);

Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("testxml.xml"), "UTF8"));
DOMBuilder builder = new DOMBuilder();
Document jdoc = builder.build(doc);
XMLOutputter fmt = new XMLOutputter();
fmt.setFormat(Format.getPrettyFormat());
fmt.output(jdoc, out);

生成此 XML 文件:

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:foo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:foo bar.xsd" />

当我使用 JDOM2 时,属性xsi:schemaLocation更改为schemaLocation(并且 XML 看起来像这样):

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:foo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" schemaLocation="urn:iso:foo bar.xsd" />

有没有办法将xsi:零件保留在 JDOM2 中?没有它,处理生成的 XML 的系统就无法读取它(不在我的控制之下)。不确定是否是同一个问题。

4

2 回答 2

2

JDOM 需要使用命名空间感知的 DOM 实现来构建 JDOM 文档。

我整理了以下代码来说明这一点:

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.jdom2.Document;
import org.jdom2.input.DOMBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;


public class DOMvsJDOM {

    private static org.w3c.dom.Document buildDOM(String xml) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        dbf.setValidating(false);
        dbf.setExpandEntityReferences(false);
        DocumentBuilder db = dbf.newDocumentBuilder();
        StringReader sr = new StringReader(xml);
        InputSource is = new InputSource(sr);
        return db.parse(is);
    }

    public static void printDocument(org.w3c.dom.Document doc, OutputStream out) throws Exception {
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

        transformer.transform(new DOMSource(doc), 
             new StreamResult(new OutputStreamWriter(out, "UTF-8")));
    }

    private static void parseUsingJDOM(org.w3c.dom.Document doc) throws Exception {
//      Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("testxml.xml"), "UTF8"));
      DOMBuilder builder = new DOMBuilder();
      Document jdoc = builder.build(doc);
      XMLOutputter fmt = new XMLOutputter();
      fmt.setFormat(Format.getPrettyFormat());
      fmt.output(jdoc, System.out);
    }

    public static void main(String[] args) throws Exception {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        org.w3c.dom.Document doc = dbFactory.newDocumentBuilder().newDocument();
        doc.setXmlVersion("1.0");

        Element root = doc.createElement("Document");

        root.setAttribute("xmlns", "urn:iso:foo");
        root.setAttribute("xsi:schemaLocation", "urn:iso:foo bar.xsd");
        root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
        doc.appendChild(root);

        printDocument(doc, System.out);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        printDocument(doc, baos);

        System.out.println("JDOM Using captured");
        parseUsingJDOM(doc);

        String xml = new String(baos.toByteArray());
        doc = buildDOM(xml);

        System.out.println("JDOM Using parsed");
        parseUsingJDOM(doc);

    }
}

请注意,该代码所做的是手动构建 DOM,输出它,从 DOM 构建 JDOM,输出,然后将 DOM 输出为字符串,将字符串重新解析为 DOM,然后从重新构建 JDOM -解析的 XML。

这是输出(我在输出中手动放置了一个换行符,以使实际的 DOM 字符串在其自己的行上具有 XML 声明):

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:foo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:foo bar.xsd"/>
JDOM Using captured
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:foo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" schemaLocation="urn:iso:foo bar.xsd" />
JDOM Using parsed
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:foo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:foo bar.xsd" />

底线是产生输出的 DOM 在技术上不是“名称空间感知的”,因此不满足 JDOM 2.0 的期望。

现在,您使用以下代码设置属性:

root.setAttribute("xmlns", "urn:iso:foo");
root.setAttribute("xsi:schemaLocation", "urn:iso:foo bar.xsd");
root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");

如果您改为使用命名空间感知版本:

    root.setAttribute("xmlns", "urn:iso:foo");
    root.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
    root.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation", "urn:iso:foo bar.xsd");
    doc.appendChild(root);

然后 JDOM 会做对。

这就是为什么 JDOM 在上面的 parsed-from-string 版本中工作的原因,因为 Parsing 是以命名空间感知的方式完成的。

因此,JDOM 有一个要求,即在处理 DOM 内容时,DOM 内容是 XML Namespace-aware 格式。这就是为什么我的测试都能正常工作的原因,因为我的 DOM 内容都是命名空间感知的。

不幸的是,这并不能解决您遇到的实际问题......它只是解释它。

在这种情况下, JDOM2应该与 JDOM 1.x 兼容,不兼容是个问题。JDOM2 正在做“正确”的事情,但它也应该做“错误”的事情,并且坚持为那些在 DOM 上定义但也没有正确声明的属性找到命名空间。

我创建了问题 138 来跟踪这个:https ://github.com/hunterhacker/jdom/issues/138

于 2014-11-05T12:28:49.630 回答
0

根据rolfl的提示,一种方法是不要混淆 DOM 和 JDOM 内容,仅使用 DOM 创建 XML 并将其保存为文件:

DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
Document doc = dbFactory.newDocumentBuilder().newDocument();
doc.setXmlVersion("1.0");

Element root = doc.createElement("Document");

root.setAttribute("xmlns", "urn:iso:foo");
root.setAttribute("xsi:schemaLocation", "urn:iso:foo bar.xsd");
root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
doc.appendChild(root);

TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setAttribute("indent-number", 2);
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(doc);
StreamResult xmlfile = new StreamResult(new BufferedWriter(new OutputStreamWriter(new FileOutputStream("testxml.xml"), "UTF8")));
transformer.transform(source, xmlfile);

但这不是一个完全有效的答案,因为问题是如何使用 JDOM2 来完成任务。

于 2014-11-04T12:47:36.833 回答