簡體   English   中英

優雅地處理C#中XML文件中的驗證錯誤

[英]Gracefully handle validation errors in a XML file in C#

描述從長邊來說有點,請忍受我。 我想處理和驗證一個巨大的XML文件,並記錄觸發了驗證錯誤的節點,並繼續處理下一個節點。 XML文件的簡化版本如下所示。

我要執行的操作是遇到任何驗證錯誤處理節點“ A”或其子節點(XMLException和XmlSchemaValidationException),我想停止處理當前節點,將錯誤和XML記錄為節點“ A”,然后繼續進行下一個操作節點“ A”。

<Root>
  <A id="A1">
     <B Name="B1">
        <C>
          <D Name="ID" >
            <E>Test Text 1</E>
          </D>
        <D Name="text" >
          <E>Test Text 1</E>
        </D>        
      </C>
    </B>
  </A>
  <A id="A2">
    <B Name="B2">
      <C>
        <D Name="id" >
          <E>Test Text 3</E>
        </D>
        <D Name="tab1_id"  >
          <E>Test Text 3</E>
        </D>
        <D Name="text" >
          <E>Test Text 3</E>
        </D>
      </C>
    </B>
</Root>

目前,我可以通過將ValidationEventHandler與XMLReader一起使用來從XmlSchemaValidationException中恢復,該方法將拋出我在XML Processing代碼中處理的異常。 但是,在某些情況下,會觸發XMLException,從而導致進程終止。

以下代碼片段說明了我正在使用的當前結構; 這很麻煩,也歡迎代碼改進建議。

    // Setting up the XMLReader
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ConformanceLevel = ConformanceLevel.Auto;
    settings.IgnoreWhitespace = true;
    settings.CloseInput = true;
    settings.IgnoreComments = true;
    settings.ValidationType = ValidationType.Schema;
    settings.Schemas.Add(null, "schema.xsd");
    settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
    XmlReader reader = XmlReader.Create("Sample.xml", settings);   
    // Processing XML
    while (reader.Read())
    if (reader.NodeType == XmlNodeType.Element)
       if (reader.Name.Equals("A"))
         processA(reader.ReadSubtree());            
    reader.Close(); 
   // Process Node A
   private static void processA(XmlReader A){
    try{
       // Perform some book-keeping 
       // Process Node B by calling processB(A.ReadSubTree())               
    }   
    catch (InvalidOperationException ex){

    }
    catch (XmlException xmlEx){

    } 
    catch (ImportException impEx){

    }
    finally{ if (A != null) A.Close(); }            
  }
  // All the lower level process node functions propagate the exception to caller.
  private static void processB(XmlReader B){
   try{
     // Book-keeping and call processC
   }
   catch (Exception ex){
    throw ex;
    }
   finally{ if (B != null) B.Close();}    
  } 
  // Validation event handler
  private static void ValidationCallBack(object sender, ValidationEventArgs e){
    String msg =  "Validation Error: " + e.Message +" at line " + e.Exception.LineNumber+
        " position number "+e.Exception.LinePosition;
    throw new ImportException(msg);
  }

當遇到XMLSchemaValidationException時,finally塊將調用close(),並且原始XMLReader定位在子樹的EndElement上,因此processA中的finally塊將導致處理下一個節點A。

但是,遇到XMlException調用close方法時,不會將原始閱讀器放在子樹的EndElement節點上,並且會拋出InvalidOperationException。

我嘗試使用諸如skip,ReadToXYZ()方法之類的方法,但是當在任何觸發異常的節點上調用這些方法時,它們總是導致InvalidOperationException的XMLExcpetion。

以下是MSDN中有關ReadSubTree方法的摘錄。

當新的XmlReader關閉時,原始XmlReader將位於子樹的EndElement節點上。 因此,如果您在book元素的開始標簽上調用ReadSubtree方法,則在讀取子樹並關閉新的XmlReader之后,原始的XmlReader將位於book元素的結束標簽上。

注意:我不能為此使用.Net 3.5,但是歡迎使用.Net 3.5建議。

看到這個問題:
XML解析器驗證報告

您需要區分格式 正確的 xml(遵循真實xml所需的規則)和有效的 xml(遵循特定xml架構給出的其他規則)。 從規格:

但是,一旦檢測到致命錯誤,處理器就不得繼續正常處理(即,不得繼續以正常方式將字符數據和有關文檔邏輯結構的信息傳遞給應用程序)。

不管是好是壞,Visual Studio附帶的xml工具都需要非常嚴格地遵循該規范,因此,如果存在格式錯誤,將無法繼續處理。 我提供的鏈接可能會為您提供其他選擇。

我幾乎像您一樣完成了此操作,除了進食和可疑使用XmlReader.Close()之外:

XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Auto;
settings.IgnoreWhitespace = true;
settings.CloseInput = true;
settings.IgnoreComments = true;
settings.ValidationType = ValidationType.Schema;
settings.Schemas.Add(null, "schema.xsd");
settings.ValidationEventHandler += ValidationCallBack;
using (XmlReader reader = XmlReader.Create("Sample.xml", settings))
{
    while (reader.Read())
    {
        if (reader.NodeType == XmlNodeType.Element &&
            reader.Name.Equals("A"))
        {
            using (
                XmlReader subtree = reader.ReadSubtree())
            {
                try {
                    processA(subtree);
                }
                catch (ImportException ex) { // log it at least }
                catch (XmlException ex) {// log this too }
                // I would not catch InvalidOperationException - too general
            }
        }
    }
}

private static void processA(XmlReader A)
{
    using (XmlReader subtree = A.ReadSubtree())
    {
        processB(subtree);
    }
}

// All the lower level process node functions propagate the exception to caller.  
private static void processB(XmlReader B)
{
}

您不希望吃掉異常,除了processA中的異常。 讓processA的調用者決定要忽略的異常-processA不應意識到這一點。 using塊相同-將它們放在外部而不是內部。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM