Подтвердить что ты не робот

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

У меня есть настольное приложение Windows, написанное на С#, которое проходит через кучу файлов 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 есть свои сложности, но я думаю, что он работает нормально; это ошибка LOAD. Я посмотрел на различные конструкторы для XDocument Load, и я исследовал некоторые другие вопросы, связанные с этим Исключением, но я смущен тем, как это предотвратить.

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

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

Ответ 1

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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