【问题标题】:Validate an XML file against local DTD file with Java使用 Java 针对本地 DTD 文件验证 XML 文件
【发布时间】:2010-11-08 22:37:49
【问题描述】:

如何根据本地存储为文件的 DTD 验证 XML 文件? XML 文件没有任何 DOCTYPE 声明(或者可能有一个应该被覆盖的声明)。我查看了this thread,但除了他们使用.NET 之外,我怀疑这是一个好的解决方案。

任何意见表示赞赏!

【问题讨论】:

    标签: java xml validation dtd


    【解决方案1】:

    在理想情况下,您可以使用Validator 进行验证。像这样的:

    SchemaFactory schemaFactory = SchemaFactory
        .newInstance(XMLConstants.XML_DTD_NS_URI);
    Schema schema = schemaFactory.newSchema(new File(
        "xmlValidate.dtd"));
    Validator validator = schema.newValidator();
    validator.validate(new StreamSource("xmlValidate.xml"));
    

    不幸的是,Sun 实现(至少从 Java 6 开始)不支持从 DTD 创建 Schema 实例。您或许能够追踪到第 3 方的实施。

    最好的办法是在使用其他机制进行解析之前更改文档以包含 DTD。


    您可以使用 transformer 插入 DTD 声明:

    TransformerFactory tf = TransformerFactory
        .newInstance();
    Transformer transformer = tf.newTransformer();
    transformer.setOutputProperty(
        OutputKeys.DOCTYPE_SYSTEM, "xmlValidate.dtd");
    transformer.transform(new StreamSource(
        "xmlValidate.xml"), new StreamResult(System.out));
    

    ...但这似乎不能替代现有的 DTD 声明。


    这个StAX 事件阅读器可以完成这项工作:

      public static class DTDReplacer extends
          EventReaderDelegate {
    
        private final XMLEvent dtd;
        private boolean sendDtd = false;
    
        public DTDReplacer(XMLEventReader reader, XMLEvent dtd) {
          super(reader);
          if (dtd.getEventType() != XMLEvent.DTD) {
            throw new IllegalArgumentException("" + dtd);
          }
          this.dtd = dtd;
        }
    
        @Override
        public XMLEvent nextEvent() throws XMLStreamException {
          if (sendDtd) {
            sendDtd = false;
            return dtd;
          }
          XMLEvent evt = super.nextEvent();
          if (evt.getEventType() == XMLEvent.START_DOCUMENT) {
            sendDtd = true;
          } else if (evt.getEventType() == XMLEvent.DTD) {
            // discard old DTD
            return super.nextEvent();
          }
          return evt;
        }
    
      }
    

    它将在文档开始后立即发送给定的 DTD 声明并丢弃旧文档中的任何内容。

    演示用法:

    XMLEventFactory eventFactory = XMLEventFactory.newInstance();
    XMLEvent dtd = eventFactory
        .createDTD("<!DOCTYPE Employee SYSTEM \"xmlValidate.dtd\">");
    
    XMLInputFactory inFactory = XMLInputFactory.newInstance();
    XMLOutputFactory outFactory = XMLOutputFactory.newInstance();
    XMLEventReader reader = inFactory
        .createXMLEventReader(new StreamSource(
            "xmlValidate.xml"));
    reader = new DTDReplacer(reader, dtd);
    XMLEventWriter writer = outFactory.createXMLEventWriter(System.out);
    writer.add(reader);
    writer.flush();
    
    // TODO error and proper stream handling
    

    请注意,XMLEventReader 可以构成执行验证的其他一些转换机制的源。


    如果您有该选项,使用 W3 架构进行验证会容易得多。

    【讨论】:

    • 非常感谢您的广泛回答,这对我很有帮助。我将研究将 DTD 转换为 W3 模式,因为我可以使用 Sun 的验证器。
    • 我的 XML 文件没有任何 DOCTYPE 声明。我正在使用 Android 中的 SAXParser 解析文件。我自己生成的DTD。如何在 SAX 解析中使用我的 DTD 验证 XML 文件?
    • @Khushbu - 如果你问一个新问题会更好。
    • 我已经问过question,但我没有得到正确的答案。
    • WstxParsingException / FileNotFoundException 如果丢弃的 DTD 系统 id 引用了不存在的文件,则会引发。在inFactory 实例化后添加inFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false)
    【解决方案2】:

    我很确定前面提到的东西会起作用..

    感谢您的帮助,但是如果没有指定 DOCTYPE 怎么办? 全部?在这种情况下,EntityResolver 不会帮助我,不是吗? – 西蒙 2009 年 7 月 8 日 6:34

    @Bluegene:如果没有 DOCTYPE,你要验证什么? – J-16 SDiZ 2009 年 7 月 8 日 7:12

    反对我自己的 DTD。我只是想确保我收到的 XML 符合我的 DTD,而不仅仅是发件人指定的任何 DTD。 – 西蒙·朱尔 2009 年 8 月 23:09

    如果问题是您希望根据您的 dtd 而不是作者对其进行验证,则应确保有明确的文档详细说明 doctype 以及 xml 文件中必须包含的内容

    【讨论】:

      【解决方案3】:

      你必须实现EntityResolver,结帐this example

      【讨论】:

      • 感谢您的帮助,但是如果根本没有指定 DOCTYPE 怎么办?在这种情况下,EntityResolver 不会帮助我,不是吗?
      • @Bluegene:如果没有 DOCTYPE,你要验证什么?
      • 反对我自己的 DTD。我只是想确保我收到的 XML 符合 my DTD,而不仅仅是发件人指定的任何 DTD。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-18
      • 2011-12-26
      • 2010-09-06
      • 1970-01-01
      • 1970-01-01
      • 2010-12-03
      相关资源
      最近更新 更多