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

在Java DOM文档中设置名称空间和前缀,java,xml,serialization,transformer,Java,Xml,Serialization,Transformer,我正在尝试将结果集转换为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 = DOMImp

我正在尝试将结果集转换为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文件,出现了几个警告。 一个是关于没有doctype。所以我尝试了另一种方法来实现这一点。我在变形金刚课上遇到过。这个类允许我设置编码、doctype等

以前的实现支持自动修复命名空间。以下内容不适用

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;
}
private static Document to Document(ResultSet rs)引发异常{
DocumentBuilderFactory工厂=DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder=factory.newDocumentBuilder();
Document doc=builder.newDocument();
URL名称空间URL=新URL(“http://www.w3.org/2001/XMLSchema-instance");
String namespace=“xmlns:xsi=“+namespaceURL.toString();
元素消息=doc.createElements(名称空间,“消息”);
文档附件子项(消息);
ResultSetMetaData rsmd=rs.getMetaData();
int colCount=rsmd.getColumnCount();
字符串attributeValue=“true”;
String attribute=“xsi:nil”;
rs.beforeFirst();
while(rs.next()){
数量=0;
元素消息=doc.createElement(“消息”);
messages.appendChild(message);

对于(int i=1;i您没有在根节点中添加名称空间声明;您只是在名称空间中声明了根节点,这是两件完全不同的事情。在构建DOM时,您需要在每个相关节点上引用名称空间。换句话说,当您添加属性时,您需要定义其名称空间(例如,setAttributeNS)


旁注:虽然XML名称空间看起来像URL,但实际上不是。这里不需要使用URL类。

在名称空间感知文档上设置节点的正确方法是使用:

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

rootNode.setAttribute("xmlns:PREFIX", "http://example/namespace");
请确保设置:

documentBuilderFactory.setNamespaceAware(true)

否则您就没有名称空间意识。

请注意,使用setAttribute设置xmlns前缀是错误的。 如果要对DOM进行eg签名,必须使用setAttributeNS:
element.setAttributeNS(“http://www.w3.org/2000/xmlns/“,”xmlns:PREFIX“,”http://example/namespace")

谢谢,它现在开始工作了。今天学到了一些新东西,就像每天一样。+1,尽管我不需要调用
setNamespaceAware
。该函数的文档表明它与解析相关。请注意,有一个
setPrefix(prefix);
允许动态设置前缀的方法。您注意到差异的任何特定示例?当我将DOM转换为文件时,使用setAttribute或SetAttributes会给出相同的输出。您注意到转换修复的运行时DOM中的一些差异吗?这可能会起作用,但您有一个硬编码的命名空间。除非我这是对标准模式的引用,我非常喜欢配置文件中的这些内容。值来自何处并不重要。重要的是,如果使用名称空间,则使用setAttributeNS。@JBert如果它仍然相关(2015):我们在使用XML签名时遇到问题,因为代码使用setAttribute而不是setAttributeNS。签名无法验证。感谢您的答复。因此
setAttributesNS
似乎可以正确操作内存中DOM以准备签名,而
setAttribute
仅在s之后提供正确的结构序列化(可能是序列化程序修复了问题)。然后,我可以看到内存中DOM上的微小差异会产生不同的XML规范化结果。验证器显然会读取序列化的XML,因此如果签名是从具有“无效”的DOM生成的说明规范化中的差异确实会导致创建无效签名。另请参阅