【问题标题】:Setting namespaces and prefixes in a Java DOM document在 Java DOM 文档中设置名称空间和前缀
【发布时间】:2012-05-22 00:23:45
【问题描述】:

我正在尝试将 ResultSet 转换为 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 文件,但出现了一些警告。 一个关于没有文档类型的问题。所以我尝试了另一种方法来实现这一点。我遇到了 Transformer 类。这个类让我设置编码、文档类型等。

之前的实现支持自动命名空间修复。以下没有。

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: Namespace for prefix 'xsi' has not been declared。如您所见,我尝试将带有 xsi 前缀的命名空间添加到文档的根元素中。经过测试,我仍然得到了异常。设置命名空间及其前缀的正确方法是什么?

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

【问题讨论】:

标签: java xml serialization transformer


【解决方案1】:

您还没有在根节点中添加命名空间声明;你刚刚在命名空间中声明了根节点,两个完全不同的东西。在构建 DOM 时,您需要引用每个相关节点上的命名空间。换句话说,当你添加你的属性时,你需要定义它的命名空间(例如,setAttributeNS)。

旁注:虽然 XML 命名空间看起来像 URL,但实际上并非如此。此处无需使用 URL 类。

【讨论】:

  • 谢谢,现在可以使用了。今天学到了一些新东西,就像每天一样。
【解决方案2】:

在 namespaceAware 文档上设置节点的正确方法是使用:

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

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

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

请务必设置:

documentBuilderFactory.setNamespaceAware(true)

否则你没有 namespaceAwareness。

【讨论】:

  • +1,虽然我不需要打电话给setNamespaceAware。该函数的文档表明它与解析有关。
  • 请注意,有一个setPrefix(prefix); 方法允许动态设置前缀。
【解决方案3】:

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

【讨论】:

  • 任何你注意到不同的具体例子?当我将 DOM 转换为文件时,使用 setAttribute 或 setAttributeNS 会给出相同的输出。您是否注意到转换修复的运行时 DOM 存在一些差异?
  • 这可能有效,但您有一个硬编码的命名空间。除非它是对标准架构的引用,否则我真的很喜欢配置文件中的这些东西。
  • 无所谓,价值从何而来。如果您使用命名空间,则使用 setAttributeNS 很重要。
  • @JBert 如果它仍然相关(2015 年):我们在使用 XML 签名时遇到问题,因为代码使用 setAttribute 而不是 setAttributeNS。无法验证签名。
  • 感谢您的回复。所以setAttributesNS 似乎正确地操纵了内存中的 DOM 以准备它进行签名,而setAttribute 仅在序列化之后才给出正确的结构(也许它是修复问题的序列化程序)。然后我可以看到内存中 DOM 上的微小差异会产生不同的 XML 规范化结果。验证器显然会读取序列化的 XML,因此如果签名是从具有“无效”状态的 DOM 生成的,那么规范化的差异确实会导致创建无效签名。
猜你喜欢
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 2010-12-02
  • 2010-12-15
  • 1970-01-01
  • 2015-04-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多