Как предотвратить System.Xml.XmlException: недопустимый символ в заданной кодировке - PullRequest
15 голосов
/ 26 ноября 2011

У меня есть настольное приложение 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 ]

37 голосов
/ 26 ноября 2011

Чтобы контролировать кодировку (когда вы знаете, что это такое), вы можете загрузить файлы, используя переопределение метода 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 .

2 голосов
/ 26 ноября 2011

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

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

1 голос
/ 26 ноября 2011

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

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

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

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

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

...