Как предотвратить System.Xml.XmlException: недопустимый символ в заданной кодировке

У меня есть настольное приложение для Windows, написанное на C#, которое просматривает кучу XML-файлов, хранящихся на диске и созданных сторонней программой. Большинство файлов загружаются и успешно обрабатываются кодом LINQ, следующим за этим утверждением:

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>();

... где inFileName - полный путь и имя файла, например:

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

Но некоторые файлы вызывают такие проблемы:

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

Файлы XML выглядят так (в этом примере показаны только 2 элемента DOCUMENT, но их много):

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

У операторов LINQ есть свои сложности, но я думаю, что это работает нормально; это НАГРУЗКА, которая терпит неудачу. Я посмотрел на различные конструкторы для XDocument Load и исследовал некоторые другие вопросы, связанные с этим исключением, но я не понимаю, как это предотвратить.

Наконец, в строке 52327, позиция 126, в файле, который не удалось загрузить, кажется, что эти данные в строке 52327 НЕ должны были вызвать проблему (а последний символ находится в позиции 103!

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

4 ответа

Решение

Чтобы контролировать кодировку (когда вы знаете, что это такое), вы можете загрузить файлы, используя Load переопределение метода, который принимает Stream,

Тогда вы можете создать новый StreamReader против вашего файла с указанием соответствующего Encoding в конструкторе.

Например, чтобы открыть файл с использованием западноевропейской кодировки, замените следующую строку кода в вопросе:

XDocument xmlDoc = XDocument.Load(inFileName);

с этим кодом:

XDocument xmlDoc = null;

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

Список поддерживаемых кодировок можно найти в документации MSDN.

Поскольку XmlDocument загружает все, как только он переходит в незашифрованный символ, он прерывает весь процесс. Если вы хотите обработать то, что можете, и пропустить / записать ненужные биты, посмотрите на XmlTextReader. XmlTextReader, загруженный из Filestream, будет загружать узел за раз, поэтому он также будет использовать намного меньше памяти. Вы можете даже стать умнее, разбить вещь и распараллелить обработку.

Когда у меня было это, там были такие вещи, как акцентированные персонажи: могила, острый, умлаут и тому подобное.

У меня нет никаких автоматизированных процессов, поэтому обычно я просто загружаю файл в Visual Studio и редактирую плохих парней до тех пор, пока не останется скандалов. Теория обоснована, хотя.

Указанный файл содержит символ, допустимый для имени файла, но недопустимый в атрибуте XML. У вас есть несколько вариантов.

  1. Вы можете изменить имя файла и перезапустить свой сторонний скрипт.
  2. Вы могли бы работать с поставщиком, чтобы обеспечить исправление, которое безопасно кодирует оскорбительные символы.
  3. Вы можете предварительно проверить документы XML и удалить ошибочные записи перед обработкой.

Не уверен, что это ваш случай, но это может быть связано с недопустимыми байтовыми последовательностями для данной кодировки. Пример: http://en.wikipedia.org/wiki/UTF-8.

Попробуйте отфильтровать неверные последовательности из файла во время загрузки.

Другие вопросы по тегам