2011-10-03 80 views
4

考慮啓動的,多模式下的XML文檔(這不是Spring特有的問題,這只是一個例子的方便XML文檔):驗證XML對多個任意模式

<beans xmlns="http://www.springframework.org/schema/beans" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:jaxrs="http://cxf.apache.org/jaxrs" 
     xmlns:osgi="http://www.springframework.org/schema/osgi" 
     xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
      http://cxf.apache.org/jaxrs 
       http://cxf.apache.org/schemas/jaxrs.xsd 
      http://www.springframework.org/schema/osgi 
       http://www.springframework.org/schema/osgi/spring-osgi.xsd"> 

我想驗證文檔,但事先並不知道文檔作者將使用哪些命名空間。我相信文檔作者,所以我願意下載任意的模式URL。我如何實現我的驗證器?

我知道我可以用一個DocumentBuilderFactory實例指定我的模式,我的調用setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", new String[] {...})但我不知道模式URL直到文檔被解析。

當然,我可以在解析文檔後自己提取XSD URL,然後通過驗證程序運行它,如上所述指定"http://java.sun.com/xml/jaxp/properties/schemaSource",但是肯定已經有一個自動執行的實現?

回答

4

原諒我回答我自己的問題...... @Eugene Yokota和@ 42的其他答案非常有幫助,但我認爲他們還不夠完整,無法接受。我需要做額外的工作來將這些建議寫入下面的最終解決方案。以下在JDK 1.6下完美工作。它沒有足夠的錯誤檢查(請參閱Eugene答案中的鏈接,這是一個非常完整的解決方案 - 但不可重複使用),我相信它也不會緩存下載的XSD。我認爲它利用了Xerces解析器的特定功能,因爲apache.org功能的URL。

InputStream xmlStream = ... 

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
    factory.setNamespaceAware(true); 
    factory.setValidating(true); 
    factory.setXIncludeAware(true); 
    factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); 
    factory.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true); 
    factory.setFeature("http://apache.org/xml/features/honour-all-schemaLocations", true); 
    factory.setFeature("http://apache.org/xml/features/validate-annotations", true); 
    factory.setFeature("http://apache.org/xml/features/generate-synthetic-annotations", true); 

    DocumentBuilder builder = factory.newDocumentBuilder(); 
    builder.setErrorHandler(new ErrorHandler() { 
     public void warning(SAXParseException exception) throws SAXException { 
      LOG.log(Level.WARNING, "parse warn: " + exception, exception); 
     } 
     public void error(SAXParseException exception) throws SAXException { 
      LOG.log(Level.SEVERE, "parse error: " + exception, exception); 
     } 
     public void fatalError(SAXParseException exception) throws SAXException { 
      LOG.log(Level.SEVERE, "parse fatal: " + exception, exception); 
     } 
    }); 

    Document doc = builder.parse(xmlStream); 
+0

請注意'setValidating(true)'導致XSD驗證。請參閱https://docs.oracle.com/javase/7/docs/api/javax/xml/parsers/DocumentBuilderFactory.html#setValidating(boolean)。 – koppor

3

我還沒有確定,但您可能會發現Use JAXP Validation API to create a validator and validate input from a DOM which contains inline schemas and multiple validation roots有用。

尤其

factory.setFeature(SCHEMA_FULL_CHECKING_FEATURE_ID, schemaFullChecking); 

factory.setFeature(HONOUR_ALL_SCHEMA_LOCATIONS_ID, honourAllSchemaLocations); 
+0

總結新的讀者:該鏈接有示例代碼,做了XML的一個非驗證解析,然後使用XPath找到根節點指定的模式,然後使用模式驗證。我想它會起作用,但它很醜陋,*當然*有人已經以圖書館形式寫了這個,是的? –

2

如果你創建了這樣一個的DocumentBuilderFactory:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
    dbf.setValidating(true); 
    dbf.setNamespaceAware(true); 
    dbf.setAttribute(
      "http://java.sun.com/xml/jaxp/properties/schemaLanguage", 
      "http://www.w3.org/2001/XMLSchema"); 

然後,您可以設置通過此工廠創建的DocumentBuilder實例的EntityResolver得到一個機會來解決指令中提到的架構位置。指定的位置將出現在systemId參數中。

我認爲構建器會自動執行此操作,而不指定解析器,但顯然不是開箱即用的。可能是由另一個特徵,屬性或屬性控制的?