Изящно обрабатывать ошибки проверки в XML-файле в C # - PullRequest
3 голосов
/ 31 марта 2009

Описание немного длиннее, пожалуйста, потерпите меня. Я хотел бы обработать и проверить огромный 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>

В настоящее время я могу выполнить восстановление из XmlSchemaValidationException с помощью ValidationEventHandler с XMLReader, который выдает исключение, которое я обрабатываю, в коде обработки XML. Однако в некоторых случаях запускается 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 поддерева, и, следовательно, блок finally в processA приведет к обработке следующего узла A.

Однако, когда XMlException встречается, вызывая метод close, он не позиционирует исходный читатель на узле EndElement поддерева, и генерируется исключение InvalidOperationException.

Я пытался использовать такие методы, как skip, ReadToXYZ (), но они неизменно приводят к XMLExcpetion InvalidOperationException при вызове на любом узле, вызвавшем исключение.

Ниже приводится выдержка из MSDN относительно метода ReadSubTree.

Когда новый XmlReader был закрыто, оригинальный XmlReader будет расположенный на узле EndElement поддерево. Таким образом, если вы назвали Метод ReadSubtree для начального тега элемент книги, после поддерева был прочитан и новый XmlReader был закрыт, оригинал XmlReader располагается на конце тега элемента книги.

Примечание. Я не могу использовать .Net 3.5 для этого, однако приветствуются предложения .Net 3.5.

Ответы [ 2 ]

5 голосов
/ 31 марта 2009

см. Этот вопрос:
Отчет о проверке парсера XML

Необходимо различать правильно сформированный xml (он соответствует правилам, которые должны быть настоящими xml) и valid xml (следует дополнительным правилам, заданным конкретной схемой xml). Из спецификации:

Однако после обнаружения фатальной ошибки процессор не должен продолжать нормальную обработку (то есть он не должен продолжать передавать символьные данные и информацию о логической структуре документа в приложение обычным способом).

Что бы там ни было, инструменты xml, включенные в Visual Studio, должны очень точно следовать этой спецификации и, следовательно, не будут продолжать обработку при наличии ошибки правильной формы. Приведенная мною ссылка может дать вам несколько альтернатив.

2 голосов
/ 31 марта 2009

Я сделал это почти так же, как вы, за исключением исключения из употребления и сомнительного использования 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 не должен знать об этом. То же самое с с использованием блоков - поместите их снаружи, а не внутри.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...