c# - valor - Cómo evitar System.Xml.XmlException: carácter no válido en la codificación dada
caracteres no válidos en la ruta de acceso xml (4)
El archivo al que se hace referencia contiene un carácter que es válido para un nombre de archivo, pero no válido en un atributo XML. Tienes pocas opciones.
- Puede cambiar el nombre del archivo y volver a ejecutar su script de terceros.
- Podría trabajar con el proveedor para proporcionar un parche que codifique de forma segura los caracteres ofensivos.
- Puede validar previamente los documentos XML y eliminar las entradas ofensivas antes del procesamiento.
Tengo una aplicación de escritorio de Windows escrita en C # que recorre un montón de archivos XML almacenados en el disco y creados por un programa de terceros. La mayoría de los archivos se cargan y procesan con éxito mediante el código LINQ que sigue a esta declaración:
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>();
... donde inFileName es una ruta de acceso completa y un nombre de archivo como:
Y:/S2Out/B0000004/Pet Tab/convert.B0000004.Pet Tab.xml
Pero algunos de los archivos causan problemas como este:
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
Los archivos XML se ven así (este ejemplo muestra solo 2 elementos DOCUMENT pero hay muchos):
<?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>
Las declaraciones LINQ tienen sus propias complejidades, pero creo que funciona bien; es la CARGA lo que falla. He examinado varios constructores para XDocument Load y he investigado algunas otras preguntas que tienen esta Excepción lanzada, pero estoy confundido acerca de cómo evitar esto.
Por último, en la línea 52327, posición 126, en el archivo que no se pudo cargar, parece que estos datos en la línea 52327 NO deberían haber causado el problema (¡y el último carácter está en la posición 103!
<FILE filename="e:/S2Out/B0000004/Pet Tab/D003710954.0001.pdf" outputpath="e:/S2Out/B0000004/Pet Tab"/>
No estoy seguro de si este es tu caso, pero esto puede estar relacionado con secuencias de bytes no válidas para una codificación determinada. Ejemplo: http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences .
Intente filtrar secuencias no válidas del archivo durante la carga.
Para controlar la codificación (una vez que sepa de qué se trata), puede cargar los archivos utilizando la anulación del método Load
que acepta una Stream
.
Luego puede crear un nuevo StreamReader
en su archivo especificando la Encoding
apropiada en el constructor.
Por ejemplo, para abrir el archivo utilizando la codificación de Europa occidental, reemplace la siguiente línea de código en la pregunta:
XDocument xmlDoc = XDocument.Load(inFileName);
con este código:
XDocument xmlDoc = null;
using (StreamReader oReader = new StreamReader(inFileName, Encoding.GetEncoding("ISO-8859-1"))) {
xmlDoc = XDocument.Load(oReader);
}
La lista de codificaciones compatibles se puede encontrar en la documentación de MSDN .
Debido a que XmlDocument carga todo tan pronto como se ejecuta en un carácter no codificado, aborta todo el proceso. Si desea procesar lo que puede y omitir / registrar los bits duff, mire XmlTextReader. XmlTextReader cargado desde Filestream cargará un nodo a la vez, por lo que también usará mucha menos memoria. Incluso puede ser inteligente y dividir la cosa y paralelizar el procesamiento.
Cuando he tenido esto, he visto cosas como personajes acentuados: tumba, agudos, diéresis, y cosas así.
No tengo ningún proceso automatizado, por lo que, por lo general, solo cargo el archivo en Visual Studio y edito a los malos hasta que ya no queda nada. La teoría es sólida sin embargo.