2009-03-30 9 views
3

La description est peu sur le côté long s'il vous plaît ours avec moi. Je voudrais traiter et valider un énorme fichier XML et enregistrer le noeud qui a déclenché l'erreur de validation et continuer le traitement du noeud suivant. Une version simplifiée du fichier XML est présentée ci-dessous. Ce que je voudrais faire est de rencontrer un noeud de traitement d'erreur de validation 'A' ou ses enfants (XMLException et XmlSchemaValidationException) Je voudrais arrêter de traiter le noeud courant consigner l'erreur et le XML pour le noeud 'A' et déplacer sur le prochain noeud «A».gérer gracieusement les erreurs de validation dans un fichier XML en C#

<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> 

Je suis actuellement en mesure de récupérer de la XmlSchemaValidationException en utilisant un ValidationEventHandler avec XMLReader qui jette une exception que je gère dans le code de traitement XML. Cependant, dans certains cas, XMLException est déclenché ce qui conduit à la fin du processus.

Les extraits suivants du code illustrent la structure actuelle que j'utilise; il est en désordre et les suggestions d'amélioration du code sont également les bienvenues.

// 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); 
    } 

Lorsqu'un XMLSchemaValidationException est rencontré le bloc finalement invoquera close() et l'XMLReader d'origine est positionné sur le EndElement du sous-arbre et par conséquent le bloc finally en processa conduira à un traitement du nœud suivant A.

Toutefois, lorsqu'une exception XMlException est rencontrée, l'invocation de la méthode close ne positionne pas le lecteur d'origine sur le noeud EndElement du sous-arbre et une exception InvalidOperationException est en cours de lancement.

J'ai essayé d'utiliser des méthodes telles que skip, ReadToXYZ(), mais celles-ci conduisent invariablement à XMLExcpetion of InvalidOperationException lorsqu'elles sont invoquées sur n'importe quel noeud qui a déclenché une exception.

Voici un extrait de MSDN concernant la méthode ReadSubTree.

Lorsque le nouveau XmlReader a été fermé, sera positionné XmlReader d'origine sur le nœud de EndElement le sous-arbre. Ainsi, si vous avez appelé la méthode ReadSubtree sur la balise de début de l'élément livre, après le sous-arbre a été lu et le nouveau XmlReader a été fermé, le XmlReader original est placé sur la balise de fin du élément de livre.

Remarque: Je ne peux pas utiliser .Net 3.5 pour cela, cependant les suggestions de .Net 3.5 sont les bienvenues.

Répondre

5

Voir cette question:
XML Parser Validation Report

Vous devez faire la distinction entre bien formé xml (il suit les règles requises pour être vrai xml) et valide xml (suit des règles supplémentaires données par un particulier schéma XML). Une fois qu'une erreur fatale est détectée, le processeur ne doit cependant pas continuer le traitement normal (il ne doit pas continuer à transmettre les données de caractère et les informations sur la structure logique du document à l'application dans la configuration normale). façon).Pour le meilleur ou pour le pire, les outils xml inclus avec Visual Studio doivent suivre cette spécification de très près, et ne continueront donc pas le traitement s'il y a une erreur de bonne forme. Le lien que j'ai fourni pourrait vous donner des alternatives.

2

Je l'ai fait presque comme vous, sauf pour l'alimentation d'exception et de l'utilisation douteuse 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) 
{ 
} 

Vous ne voulez pas manger les exceptions, à l'exception de ceux de processa. Laissons l'appelant de processA décider des exceptions à ignorer - processA ne devrait pas en être conscient. Même avec le en utilisant blocs - les mettre à l'extérieur, plutôt que l'intérieur.

+0

Je traitais l'exception supprimé le code pour le garder concis. J'ai besoin d'effectuer quelques autres actions dans le bloc finally, donc je n'ai vu aucun avantage à utiliser le bloc "using". – welllifeisunfair

Questions connexes