使用DOM解析xml,DOCTYPE将被删除

在编辑xml时,如何用java擦除doctype?

得到这个xml文件:

 <!DOCTYPE map[   ]>  test1 test1 test1  

我的function很基础:

 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,但这些解决方案都没有…

谢谢 !

(这种反应只是对@Grzegorz Szpetkowski的答案的补充,为什么它有效)

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

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

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

     ]>  test1 test1 test1  

由于@DevNull写的是完全有效的,你不能写test1 (但是对于Java来说,即使有这个问题也能正常工作)。


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

   new value test1 test1  

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

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

结果(示例)文件:

    new value test1 test1  

编辑:

我不认为可以使用Transformer类保存内联DTD( 在这里video)。 如果你不能使用外部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的输出(我看不到使用LSSerializer添加standalone="yes"任何选项…):

     ]>  new value test1 test1  

另一种方法是使用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); 

结果:

     ]>  new value test1 test1  

@Grzegorz Szpetkowski使用外部DTD是一个好主意。 但是,如果保留这些station / @ id值,则XML仍然无效。

具有“ID”类型的任何属性都不能具有以数字开头的值。 你必须添加一些东西,比如站点的“s”:

    ]>  test1 test1 test1  

我有几乎相同的问题,发现这适用于变换。 它是有限的,因为它只允许引用dtd,如果文档的doctype可以变化,它将需要一些工作。 在我的情况下这已经足够了,我只需要在转换后对xhtml doctype进行硬编码。

 xformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "publicId"); xformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "systemId");