2016-08-04 26 views
2

我從服務器獲取任意XML並使用此Java代碼解析它:如何使XML解析器知道所有字符實體引用?

String xmlStr; // arbitrary XML input 
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
try { 
    DocumentBuilder builder = factory.newDocumentBuilder(); 
    InputSource is = new InputSource(new StringReader(xmlStr)); 
    return builder.parse(is); 
} 
catch (SAXException | IOException | ParserConfigurationException e) { 
    LOGGER.error("Failed to parse XML.", e); 
} 

在每隔一段時間,XML輸入包含像 一些未知的實體引用和失敗,出現錯誤,如org.xml.sax.SAXParseException: The entity "nbsp" was referenced, but not declared.

我可以通過預處理原始xmlStr並在解析之前轉換所有有問題的實體引用來解決此問題。這裏是一個可行的虛擬實現:

protected static String translateEntityReferences(String xml) { 
    String newXml = xml; 
    Map<String, String> entityRefs = new HashMap<>(); 
    entityRefs.put("&nbsp;", "&#160;"); 
    entityRefs.put("&laquo;", "&#171;"); 
    entityRefs.put("&raquo;", "&#187;"); 
    // ... and 250 more... 
    for(Entry<String, String> er : entityRefs.entrySet()) { 
     newXml = newXml.replace(er.getKey(), er.getValue()); 
    } 
    return newXml; 
} 

然而,這實在是不能令人滿意的,因爲有are a huge number of entity references我不希望所有的硬編碼到我的Java類。

是否有任何簡單的方法來教導整個DocumentBuilder字符實體引用列表?

+0

這裏你去:https://dev.w3.org/html5/html-author/charref玩得開心! –

+0

看起來很有趣,但我如何說服我的DocumentBuilder相同呢? ;-) – dokaspar

+0

你可以試試這個正則表達式來替換空白字符串的匹配內容。 String regexex =「&|#| [A-Za-z]?(\\ w + | \\ d +);」; Pattern pattern = Pattern.compile(regexex);否則你可以嘗試JSOUP庫。檢查鏈接[http://stackoverflow.com/questions/36026353/parsing-xml-file-containing-html-entities-in-java-without-changing-the-xml](http://stackoverflow.com/questions/36026353 /解析的XML含文件-HTML實體功能於Java的不變化的最XML)。 –

回答

1

如果您可以更改代碼以使用StAX而不是DOM,則通常使用XMLInputFactory屬性IS_REPLACING_ENTITY_REFERENCES設置爲false

public static void main(String[] args) throws Exception 
{ 
    String doc = "<doc>&nbsp;</doc>"; 
    ByteArrayInputStream is = new ByteArrayInputStream(doc.getBytes()); 

    XMLInputFactory xif = XMLInputFactory.newFactory(); 
    xif.setProperty(javax.xml.stream.XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false); 
    XMLStreamReader xr = xif.createXMLStreamReader(is); 

    while(xr.hasNext()) 
    { 
     int t = xr.getEventType(); 
     switch(t) { 
      case XMLEvent.ENTITY_REFERENCE: 
       System.out.println("Entity: "+ xr.getLocalName()); 
       break; 
      case XMLEvent.START_DOCUMENT: 
       System.out.println("Start Document"); 
       break; 
      case XMLEvent.START_ELEMENT: 
       System.out.println("Start Element: " + xr.getLocalName()); 
       break; 
      case XMLEvent.END_DOCUMENT: 
       System.out.println("End Document"); 
       break; 
      case XMLEvent.END_ELEMENT: 
       System.out.println("End Element: " + xr.getLocalName()); 
       break; 
      default: 
       System.out.println("Other: "); 
       break; 
     } 
     xr.next(); 
    } 
} 

輸出:

Start Document 
Start Element: doc 
Entity: nbsp null 
End Element: doc 

但是,可能需要在你的代碼太多重寫,如果你真的需要完整的DOM樹在內存中。

我花了一個小時追蹤DOM實現,並找不到任何方法使DOM解析器從XMLStreamReader中讀取。

此外在代碼中有證據表明內部DOM解析器實現有一個類似於IS_REPLACING_ENTITY_REFERENCES的選項,但我找不到從外部設置它的任何方法。

+0

感謝您的調查。但確實,這看起來像一個重寫... – dokaspar

+0

真正令人難過的部分是,代碼掃描實體引用並拋出異常('com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEntityReference( XMLStringBuffer)')實際上會檢查一個'fReplaceEntityReferences'選項標誌。如果我在調試器中手動將其調整爲'false',代碼將按照您的需要構建DOM。但似乎沒有辦法從公共API中設置它,也無法訪問實現。 –

相關問題