在 Java DOM 文档中设置命名空间和前缀

2022-09-03 16:34:47

我正在尝试将结果集转换为 XML 文件。我首先将此示例用于序列化。

import  org.w3c.dom.bootstrap.DOMImplementationRegistry;
import  org.w3c.dom.Document;
import  org.w3c.dom.ls.DOMImplementationLS;
import  org.w3c.dom.ls.LSSerializer;

...

DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();

DOMImplementationLS impl = 
    (DOMImplementationLS)registry.getDOMImplementation("LS");

...     

LSSerializer writer = impl.createLSSerializer();
String str = writer.writeToString(document);

完成这项工作后,我试图验证我的XML文件,有几个警告。一个关于没有文档类型。所以我尝试了另一种方法来实现这一点。我遇到了变形金刚类。这个类允许我设置编码,文档类型等。

前面的实现支持自动命名空间修复。以下情况则不然。

private static Document toDocument(ResultSet rs) throws Exception {   
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true);
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document doc = builder.newDocument();

    URL namespaceURL = new URL("http://www.w3.org/2001/XMLSchema-instance");
    String namespace = "xmlns:xsi="+namespaceURL.toString();

    Element messages = doc.createElementNS(namespace, "messages");
    doc.appendChild(messages);

    ResultSetMetaData rsmd = rs.getMetaData();
    int colCount = rsmd.getColumnCount();

    String attributeValue = "true";
    String attribute = "xsi:nil";

    rs.beforeFirst();

    while(rs.next()) {
        amountOfRecords = 0;
        Element message = doc.createElement("message");
        messages.appendChild(message);

        for(int i = 1; i <= colCount; i++) {

            Object value = rs.getObject(i);
            String columnName = rsmd.getColumnName(i);

            Element messageNode = doc.createElement(columnName);

            if(value != null) {
                messageNode.appendChild(doc.createTextNode(value.toString()));
            } else {
                messageNode.setAttribute(attribute, attributeValue);
            }
            message.appendChild(messageNode);
        }
        amountOfRecords++;
    }
    logger.info("Amount of records archived: " + amountOfRecords);

    TransformerFactory tff = TransformerFactory.newInstance();
    Transformer tf = tff.newTransformer();
    tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    tf.setOutputProperty(OutputKeys.INDENT, "yes");

    BufferedWriter bf = createFile();
    StreamResult sr = new StreamResult(bf);
    DOMSource source = new DOMSource(doc);
    tf.transform(source, sr);

    return doc;
}

当我测试以前的实现时,我得到了一个TransformationException:前缀“xsi”的命名空间尚未声明。如您所见,我已尝试将具有 xsi 前缀的命名空间添加到文档的根元素中。测试后,我仍然得到异常。设置命名空间及其前缀的正确方法是什么?

编辑:我对第一个实现的另一个问题是XML文档中的最后一个元素没有最后三个结束标记。


答案 1

在命名空间上设置节点的正确方法是使用:

rootNode.createElementNS("http://example/namespace", "PREFIX:aNodeName");

因此,您可以将“PREFIX”替换为您自己的自定义前缀,并将“aNodeName”替换为节点名称。若要避免每个节点都有自己的命名空间声明,可以将命名空间定义为根节点上的属性,如下所示:

rootNode.setAttribute("xmlns:PREFIX", "http://example/namespace");

请务必设置:

documentBuilderFactory.setNamespaceAware(true)

否则,您没有命名空间防范性。


答案 2

请注意,使用 setAttribute 设置 xmlns 前缀是错误的。如果你想签署你的DOM,你必须使用setAttributeNS:element.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:PREFIX", "http://example/namespace");