Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
256 views
in Technique[技术] by (71.8m points)

Validate an XML file against local DTD file with Java

How can I validate an XML file against a DTD that is stored locally as a file? The XML file does not have any DOCTYPE declaration (or may have one that should then be overridden). I had a look at this thread but besides the fact they are using .NET I doubt that this is a good solution.

Any input appreciated!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In an ideal world, you'd be able to validate using a Validator. Something like this:

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"));

Unfortunately, the Sun implementation (at least, as of Java 6) does not include support for creating a Schema instance from a DTD. You might be able to track down a 3rd party implementation.

Your best bet may be to alter the document to include the DTD before parsing using some other mechanism.


You can use a transformer to insert a DTD declaration:

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));

...but this does not seem to replace an existing DTD declaration.


This StAX event reader can do the job:

  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;
    }

  }

It will send a given DTD declaration right after the document start and discard any from the old document.

Demo usage:

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

Note that the XMLEventReader could form the source for some other transformation mechanism that performed validation.


It would be much easier to validate using a W3 schema if you have that option.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...