使用 Java 根据本地 DTD 文件验证 XML 文件

2022-09-01 20:59:47

如何根据本地存储为文件的 DTD 验证 XML 文件?XML 文件没有任何 DOCTYPE 声明(或者可能具有随后应重写的声明)。我看了一下这个线程,但除了他们正在使用.NET之外,我怀疑这是一个很好的解决方案。

任何输入赞赏!


答案 1

在理想情况下,您将能够使用验证器进行验证。像这样:

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 实例的支持。您也许能够跟踪第三方实现。

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


可以使用转换器插入 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 架构进行验证会容易得多。


答案 2

我很确定上述东西会起作用。

感谢您的帮助,但是如果根本没有指定DOCTYPE怎么办?在这种情况下,实体解析器不会帮助我,不是吗?– 西蒙 七月 8 '09 在 6:34

@Bluegene:如果没有DOCTYPE,您正在验证什么?– J-16 SDiZ Jul 8 '09 at 7:12

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

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


推荐