2016-08-04 73 views
3

經過幾天的混淆,並嘗試使用XSD驗證XML文檔的不同庫(我100%肯定是有效的根據XSD),我終於發現,原因是org.w3c.dom.DocumentBuilderorg.w3c.dom.Document決定將一堆屬性潛入DOM中。以下是我收到的許多驗證錯誤之一:爲什麼org.w3c.dom.DocumentBuilder偷偷向XML元素添加屬性

元素'API_Version'的屬性'high_value_range'的值'127'對於相應的屬性使用無效。屬性'high_value_range'具有固定值'4294967295'。

正如您在此示例test.xml文件中看到,我指定屬性'high_value_range'

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE My_Doc SYSTEM "MyDoc.dtd"> 
<My_Doc xmlns="http://my.namespace.org"> 
    <Package> 
    <API_Version>1</API_Version> 
    </Package> 
</My_Doc> 

這裏是解析XML文件和打印DOM代碼:

package client; 

import java.io.*; 
import javax.xml.parsers.*; 
import javax.xml.transform.*; 
import javax.xml.transform.dom.*; 
import javax.xml.transform.stream.*; 
import org.w3c.*; 
import org.w3c.dom.*; 
import org.xml.sax.ErrorHandler; 
import org.xml.sax.SAXException; 
import org.xml.sax.SAXParseException; 

public class Writer 
{ 
    //method to convert Document to String 
    public static String getStringFromDocument(Document doc) 
    { 
     try 
     { 
      DOMSource domSource = new DOMSource(doc); 
      StringWriter writer = new StringWriter(); 
      StreamResult result = new StreamResult(writer); 
      TransformerFactory tf = TransformerFactory.newInstance(); 
      javax.xml.transform.Transformer transformer = tf.newTransformer(); 
      transformer.transform(domSource, result); 
      return writer.toString(); 
     } 
     catch(TransformerException ex) 
     { 
      ex.printStackTrace(); 
      return null; 
     } 
    } 

    public static void main(String[] args) throws SAXException, IOException, ParserConfigurationException 
    { 
     DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); 
     DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); 
     Document doc; 
     doc = dBuilder.parse(new File("data/test.xml")); 
     System.out.println(getStringFromDocument(doc)); 

    } 
} 

最後,打印出解析的Document並導致後續驗證失敗的結果:

<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<My_Doc xmlns="http://my.namespace.org" element_tag="GROUP" element_type="GROUP"> 
    <Package> 
    <API_Version element_type="FIELD" field_type="INTEGER" value_upper_range="127">1</API_Version> 
    </Package> 
</My_Doc> 

這裏是MyDoc.dtd重現:

<?xml version='1.0' encoding='UTF-8' ?> 
<?My_Application DTD_Version='6.5'?> 
<!ELEMENT My_Doc (Package)> 
<!ATTLIST My_Doc element_tag CDATA #FIXED 'GROUP' 
       element_type CDATA #FIXED 'GROUP' 
       xmlns  CDATA #FIXED 'http://my.namespace.org' > 

<!ELEMENT Package (API_Version)> 

<!ELEMENT API_Version (#PCDATA)> 
<!ATTLIST API_Version element_type  CDATA #FIXED 'FIELD' 
            field_type  CDATA #FIXED 'INTEGER' 
            high_value_range CDATA #FIXED '127' > 
<?DTD_End Dummy_Processing_Instruction='END'?> 

爲什麼DocumentBuilder將所有多餘的東西以及如何從這樣做,阻止它?

+0

這聽起來不太可能。請提供[mcve],以便我們自己重現問題。 (出於興趣,如果刪除DTD,是否會改變任何內容?) –

+0

@JonSkeet我無法共享數據,DTD或XSD。充其量我可以分享所有的代碼,如果你認爲這將有所幫助,但我提供的東西除了像'Class',try/catch','main','import'等垃圾。是的,如果我刪除在XML文件中引用DTD,那麼'DocumentBuilder'不會添加所有這些東西。 –

+0

而且我同意這聽起來不太可能,我對你感到困惑,一定是一個很好的問題來破壞你;) –

回答

3

當您在啓用DTD驗證的情況下解析文檔時,將DTD中定義的默認屬性值插入解析的文檔中。如果您不希望發生這種情況,那麼要麼不定義DTD,要麼禁止DTD驗證,或者禁止擴展DTD定義的屬性默認值。 (如果你使用的TransformerFactory是Saxon,但是不適用於Xalan,我可以告訴你該怎麼做)。

+0

在解析文檔之前,我添加了'dbFactory.setValidating(false);'但即使在驗證被禁用的情況下,它仍然會添加額外的元素。不幸的是,我需要定義DTD,因此刪除這不是一個選項。 –

+0

好吧,我發現如果我做'dbFactory.setValidating(false)'*和*'dbFactory.setFeature(「http://apache.org/xml/features/nonvalidating/load-external-dtd」,false)'那麼它不會添加額外的屬性。但問題是隨後的'Validator.validate'調用將不會嘗試使用XSD驗證XML文檔。 –

+0

當您說「當您啓用DTD驗證解析文檔時,將在DTD中定義的默認屬性值插入解析的文檔中。」你是專指'Javax.xml'庫還是更普遍?因爲我使用Xerces在C++中創建了一個類似的應用程序,並且在不擴展DTD定義的屬性的情況下對其進行了適當驗證。 –