web-dev-qa-db-fra.com

Comment empêcher System.Xml.XmlException: caractère non valide dans l'encodage donné

J'ai une application de bureau Windows écrite en C # qui parcourt un tas de fichiers XML stockés sur le disque et créés par un programme tiers. La plupart de tous les fichiers sont chargés et traités avec succès par le code LINQ qui suit cette instruction:

XDocument xmlDoc = XDocument.Load(inFileName);
List<DocMetaData> docList =
      (from d in xmlDoc.Descendants("DOCUMENT")
       select new DocMetaData
       {
      File = d.Element("FILE").SafeGetAttributeValue("filename")
         ,
      Folder = d.Element("FOLDER").SafeGetAttributeValue("name")
         ,
      ItemID = d.Elements("INDEX")
          .Where(i => (string)i.Attribute("name") == "Item ID(idmId)")
          .Select(i => (string)i.Attribute("value"))
          .FirstOrDefault()
         ,
      Comment = d.Elements("INDEX")
          .Where(i => (string)i.Attribute("name") == "Comment(idmComment)")
          .Select(i => (string)i.Attribute("value"))
          .FirstOrDefault()
         ,
      Title = d.Elements("INDEX")
          .Where(i => (string)i.Attribute("name") == "Title(idmName)")
          .Select(i => (string)i.Attribute("value"))
          .FirstOrDefault()
         ,
      DocClass = d.Elements("INDEX")
          .Where(i => (string)i.Attribute("name") == "Document Class(idmDocType)")
          .Select(i => (string)i.Attribute("value"))
          .FirstOrDefault()
       }
      ).ToList<DocMetaData>();

... où inFileName est un chemin d'accès complet et un nom de fichier tels que:

     Y:\S2Out\B0000004\Pet Tab\convert.B0000004.Pet Tab.xml

Mais quelques-uns des fichiers provoquent des problèmes comme celui-ci:

System.Xml.XmlException: Invalid character in the given encoding. Line 52327, position 126.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.Throw(String res, String arg)
at System.Xml.XmlTextReaderImpl.InvalidCharRecovery(Int32& bytesCount, Int32& charsCount)
at System.Xml.XmlTextReaderImpl.GetChars(Int32 maxCharsCount)
at System.Xml.XmlTextReaderImpl.ReadData()
at System.Xml.XmlTextReaderImpl.ParseAttributeValueSlow(Int32 curPos, Char quoteChar, NodeData attr)
at System.Xml.XmlTextReaderImpl.ParseAttributes()
at System.Xml.XmlTextReaderImpl.ParseElement()
at System.Xml.XmlTextReaderImpl.ParseElementContent()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.Linq.XContainer.ReadContentFrom(XmlReader r)
at System.Xml.Linq.XContainer.ReadContentFrom(XmlReader r, LoadOptions o)
at System.Xml.Linq.XDocument.Load(XmlReader reader, LoadOptions options)
at System.Xml.Linq.XDocument.Load(String uri, LoadOptions options)
at System.Xml.Linq.XDocument.Load(String uri)
at CBMI.WinFormsUI.GridForm.processFile(StreamWriter oWriter, String inFileName, Int32 XMLfileNumber) in C:\ProjectsVS2010\CBMI.LatitudePostConverter\CBMI.LatitudePostConverter\CBMI.WinFormsUI\GridForm.cs:line 147
at CBMI.WinFormsUI.GridForm.btnProcess_Click(Object sender, EventArgs e) in C:\ProjectsVS2010\CBMI.LatitudePostConverter\CBMI.LatitudePostConverter\CBMI.WinFormsUI\GridForm.cs:line 105

Les fichiers XML ressemblent à ceci (cet exemple ne montre que 2 éléments DOCUMENT mais il y en a beaucoup):

<?xml version="1.0" ?>
<DOCUMENTCOLLECTION>
   <DOCUMENT>
       <FILE filename="e:\S2Out\B0000005\General\D003712420.0001.pdf" outputpath="e:\S2Out\B0000005\General"/>
       <ANNOTATION filename=""/>
       <INDEX name="Comment(idmComment)" value=""/>
       <INDEX name="Document Class(idmDocType)" value="General"/>
       <INDEX name="Item ID(idmId)" value="003712420"/>
       <INDEX name="Original File Name(idmDocOriginalFile)" value="Matrix Aligning 603.24 Criteria to Petition Pages.pdf"/>
       <INDEX name="Title(idmName)" value="Matrix for 603.24"/>
       <FOLDER name="/Accreditation/PASBVE/2004-06"/>
   </DOCUMENT>
   <DOCUMENT>
       <FILE filename="e:\S2Out\B0000005\General\D003712442.0001.pdf" outputpath="e:\S2Out\B0000005\General"/>
       <ANNOTATION filename=""/>
       <INDEX name="Comment(idmComment)" value=""/>
       <INDEX name="Document Class(idmDocType)" value="General"/>
       <INDEX name="Item ID(idmId)" value="003712442"/>
       <INDEX name="Original File Name(idmDocOriginalFile)" value="Contacts at NDU.pdf"/>
       <INDEX name="Title(idmName)" value="Contacts at NDU"/>
       <FOLDER name="/Accreditation/NDU/2006-12/Self-Study"/>
   </DOCUMENT>

Les instructions LINQ ont leurs propres complexités mais je pense que cela fonctionne bien; c'est la CHARGE qui échoue. J'ai regardé les différents constructeurs pour XDocument Load et j'ai recherché d'autres questions ayant cette exception levée, mais je ne sais pas comment empêcher cela.

Enfin, à la ligne 52327, position 126, dans le fichier qui n'a pas pu être chargé, il semble que ces données de la ligne 52327 n'auraient PAS dû causer le problème (et le dernier caractère est à la position 103!

<FILE filename="e:\S2Out\B0000004\Pet Tab\D003710954.0001.pdf" outputpath="e:\S2Out\B0000004\Pet Tab"/>
16
John Adams

Afin de contrôler l'encodage (une fois que vous savez de quoi il s'agit), vous pouvez charger les fichiers à l'aide de la méthode Load qui remplace un Stream.

Ensuite, vous pouvez créer un nouveau StreamReader contre votre fichier en spécifiant le Encoding approprié dans le constructeur.

Par exemple, pour ouvrir le fichier à l'aide du codage d'Europe occidentale, remplacez la ligne de code suivante dans la question:

XDocument xmlDoc = XDocument.Load(inFileName);

avec ce code:

XDocument xmlDoc = null;

using (StreamReader oReader = new StreamReader(inFileName, Encoding.GetEncoding("ISO-8859-1"))) {
    xmlDoc = XDocument.Load(oReader);
}

La liste des encodages pris en charge se trouve dans la documentation MSDN .

39
competent_tech

Je ne sais pas si c'est votre cas, mais cela peut être lié à des séquences d'octets invalides pour un codage donné. Exemple: http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences .

Essayez de filtrer les séquences invalides du fichier pendant le chargement.

2
Igor S.

Étant donné que XmlDocument charge l'intégralité de l'élément dès qu'il rencontre un caractère non codé, il abandonne l'ensemble du processus. Si vous souhaitez traiter ce que vous pouvez et ignorer/enregistrer les bits de duff, consultez XmlTextReader. XmlTextReader chargé à partir d'un Filestream chargera un nœud à la fois, il utilisera donc beaucoup moins de mémoire. Vous pourriez même devenir intelligent et diviser la chose et paralléliser le traitement.

Quand j'ai eu ça, ça a été des choses comme des caractères accentués là-dedans: Grave, aigus, trémas, etc.

Je n'ai pas de processus automatisé, donc généralement je charge simplement le fichier dans Visual Studio et édite les méchants jusqu'à ce qu'il n'y ait plus de squigglies. Mais la théorie est solide.

1
Tony Hopkinson

Le fichier référencé contient un caractère valide pour un nom de fichier, mais non valide dans un attribut XML. Vous avez quelques options.

  1. Vous pouvez modifier le nom de fichier et réexécuter votre script tiers.
  2. Vous pouvez travailler avec le fournisseur pour fournir un correctif qui code en toute sécurité les personnages incriminés.
  3. Vous pouvez pré-valider les documents XML et supprimer les entrées incriminées avant le traitement.
1
phatfingers