2009-07-08 152 views
23

如何根據本地存儲爲文件的DTD驗證XML文件? XML文件沒有任何DOCTYPE聲明(或者可能有一個應該被覆蓋)。我看了一下this thread,但除了他們使用.NET的事實之外,我懷疑這是一個很好的解決方案。使用Java根據本地DTD文件驗證XML文件

任何輸入讚賞!

回答

25

在理想的世界中,您可以使用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實例的支持。您可能能夠追蹤第三方實施。

最好的辦法可能是在使用其他一些機制解析之前,將文檔改爲包含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模式進行驗證會容易得多。

1

您必須實施EntityResolver,結帳this example

+0

感謝您的幫助,但如果沒有指定DOCTYPE,該怎麼辦?在這種情況下EntityResolver不會幫助我,是嗎? – Simon 2009-07-08 06:34:19

+0

@Bluegene:如果沒有DOCTYPE,你在驗證什麼? – 2009-07-08 07:12:36

3

即時通訊相當肯定,如果沒有DOCTYPE已在 全部被指定的東西,上述將工作..

感謝您的幫助,但什麼?在這種情況下EntityResolver不會幫助我,是嗎? - Simon Simon 09年8月8日在6:34

@Bluegene:什麼是驗證,如果沒有DOCTYPE? - J-16 SDiZ 09年7月8日7:12

對我自己的DTD。我只想確保我收到的XML 符合我的DTD,而不僅僅是發件人指定的任何DTD。 - 西蒙月 8 '09在23:09

如果問題是你希望它針對您的DTD進行驗證,而不是作家,你應該確保有清晰的文檔,詳細文檔類型,以及哪些必須在xml文件中