在编辑xml时,如何使用java擦除doctype?
得到了这个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被删除了!我只得到整个文档,但没有doctype部分,这对我很重要,因为它允许我通过id检索! 我们怎样才能保留doctype?它为什么要抹掉它? 我尝试了很多解决方案,例如outputkeys或omImpl.createDocumentType,但这些都没有...
谢谢你!答案 0 :(得分:11)
您的输入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写的是完全有效的,你不能写<station id="5">test1</station>
(但是对于Java来说,即使有这个问题也能正常工作)。
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
类(视频here)保存内联DTD是不可能的。如果您不能使用外部DTD引用,那么您可以改为使用DOM Level 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>
答案 1 :(得分:8)
(这种回应只是对@Grzegorz Szpetkowski的回答的补充,为什么会有效)
您丢失了doctype定义,因为您使用生成XSL转换的Transform
类。 XSLT树模型中没有DOCTYPE
声明或docytype定义对象/节点。当解析器将文档移交给XSLT处理器时,doctype信息将丢失,因此无法保留或复制。 XSLT提供了对输出树序列化的一些控制,包括添加带有公共或系统标识符的<!DOCTYPE ... >
声明。这些标识符的值需要事先知道,不能从输入树中读取。也不支持创建或保留嵌入式DTD或实体声明(尽管此障碍的一种解决方法是将其作为带有disable-output-escaping="yes"
的文本输出。)
为了保留DTD,您需要使用XML序列化程序而不是XSL转换来输出文档,就像Grzegorz已经建议的那样。
答案 2 :(得分:2)
@Grzegorz Szpetkowski使用外部DTD是一个好主意。但是,如果保留这些station / @ id值,XML仍然无效。
任何类型为“ID”的属性都不能包含以数字开头的值。你必须添加一些内容,比如“s”for station:
<!DOCTYPE favoris [
<!ELEMENT favoris (station*) >
<!ELEMENT station (#PCDATA) >
<!ATTLIST station
id ID #REQUIRED >
]>
<favoris>
<station id="s5">test1</station>
<station id="s6">test1</station>
<station id="s8">test1</station>
</favoris>
答案 3 :(得分:0)
我遇到了几乎相同的问题,发现this适用于变换。它是有限的,因为它只允许引用dtd,如果文档的doctype可以变化,它将需要一些工作。在我的情况下这已经足够了,我只需要在转换后对xhtml doctype进行硬编码。
xformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "publicId");
xformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "systemId");