使用 DOM 解析 xml,DOCTYPE 被擦除

2022-09-04 01:52:36

为什么 dom 与 java 在编辑 xml 时会擦除文档类型?

得到这个xml文件:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE map[ <!ELEMENT map (station*) >
                <!ATTLIST station  id   ID    #REQUIRED> ]>
<favoris>
<station id="5">test1</station>
<station id="6">test1</station>
<station id="8">test1</station>
</favoris> 

我的功能非常基本:

public static void EditStationName(int id, InputStream is, String path, String name) throws ParserConfigurationException, SAXException, IOException, TransformerFactoryConfigurationError, TransformerException{
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

    DocumentBuilder builder = factory.newDocumentBuilder();
    Document dom = builder.parse(is);

    Element e = dom. getElementById(String.valueOf(id));
    e.setTextContent(name);
    // Write the DOM document to the file
    Transformer xformer = TransformerFactory.newInstance().newTransformer();
    FileOutputStream fos = new FileOutputStream(path);
    Result result = new StreamResult(fos);  
    Source source = new DOMSource(dom);


        xformer.setOutputProperty(
                OutputKeys.STANDALONE,"yes"     
                );

    xformer.transform(source, result);
}

它正在工作,但文档类型被删除了!我刚刚得到了整个文档,但没有doctype部分,这对我来说很重要,因为它允许我通过id检索!我们如何保留文档类型?为什么它会删除它?我尝试了许多使用输出键的解决方案,例如omImpl.createDocumentType,但这些都不起作用...

谢谢!


答案 1

您的输入 XML 无效。那应该是:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE favoris [
    <!ELEMENT favoris (station)+>
    <!ELEMENT station (#PCDATA)>
    <!ATTLIST station id ID #REQUIRED>
]>
<favoris>
    <station id="i5">test1</station>
    <station id="i6">test1</station>
    <station id="i8">test1</station>
</favoris>

由于@DevNull写为完全有效,因此您无法编写(但是对于Java,即使存在该问题,它仍然可以工作)。<station id="5">test1</station>


DOCTYPE在输出 XML 文档中被擦除:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<favoris>
    <station id="i5">new value</station>
    <station id="i6">test1</station>
    <station id="i8">test1</station>
</favoris>

我还没有找到丢失DTD的解决方案,但作为解决方法,您可以设置外部DTD:

xformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "favoris.dtd");

结果(示例)文档:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE favoris SYSTEM "favoris.dtd">
<favoris>
    <station id="i5">new value</station>
    <station id="i6">test1</station>
    <station id="i8">test1</station>
</favoris>

编辑:

我不认为使用Transformer类保存内联DTD(此处为vide)。如果不能使用外部 DTD 引用,则可以改为使用 DOM 级别 3 LSSerializer 类:

DOMImplementationLS domImplementationLS =
    (DOMImplementationLS) dom.getImplementation().getFeature("LS","3.0");
LSOutput lsOutput = domImplementationLS.createLSOutput();
FileOutputStream outputStream = new FileOutputStream("output.xml");
lsOutput.setByteStream((OutputStream) outputStream);
LSSerializer lsSerializer = domImplementationLS.createLSSerializer();
lsSerializer.write(dom, lsOutput);
outputStream.close();

输出所需的DTD(我看不到任何使用...添加的选项):standalone="yes"LSSerializer

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE favoris [<!ELEMENT favoris (station)+>
<!ELEMENT station (#PCDATA)>
<!ATTLIST station id ID #REQUIRED>
]>
<favoris>
    <station id="i5">new value</station>
    <station id="i6">test1</station>
    <station id="i8">test1</station>
</favoris> 

另一种方法是使用Apache Xerces2-J类:XMLSerializer

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
...

XMLSerializer serializer = new XMLSerializer();
serializer.setOutputCharStream(new java.io.FileWriter("output.xml"));
OutputFormat format = new OutputFormat();
format.setStandalone(true);
serializer.setOutputFormat(format);
serializer.serialize(dom);

结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE favoris [<!ELEMENT favoris (station)+>
<!ELEMENT station (#PCDATA)>
<!ATTLIST station id ID #REQUIRED>
]>
<favoris>
    <station id="i5">new value</station>
    <station id="i6">test1</station>
    <station id="i8">test1</station>
</favoris>

答案 2

(这个回答在某种程度上只是对@Grzegorz Szpetkowski的答案的补充,为什么它有效)

您丢失了文档类型定义,因为您使用的类生成 XSL 转换。XSLT 树模型中没有声明或 docytype 定义对象/节点。当分析器将文档移交给 XSLT 处理器时,文档类型信息将丢失,因此无法保留或复制。XSLT 提供了对输出树序列化的一些控制,包括添加具有公共或系统标识符的声明。这些标识符的值需要事先知道,并且无法从输入树中读取。也不支持创建或保留嵌入的 DTD 或实体声明(尽管此障碍的一种解决方法是将其输出为文本, )。TransformDOCTYPE<!DOCTYPE ... >disable-output-escaping="yes"

为了保留DTD,您需要使用XML序列化程序而不是XSL转换来输出文档,就像Grzegorz已经建议的那样。