2011-11-24 14 views
13

.NET XmlTextWriter crea archivos xml no válidos.XmlTextWriter escribir caracteres de control incorrectamente

En XML, algunos caracteres de control están permitidos, como 'pestaña horizontal' (	), pero otros no, como 'pestaña vertical' (). (Consulte spec.)

Tengo una cadena que contiene un carácter de control UTF-8 que no está permitido en XML.
Aunque XmlTextWriter escapa del caracter, el XML resultante aún no es válido.

¿Cómo puedo asegurarme de que XmlTextWriter nunca produzca un archivo XML ilegal?

O, si no es posible hacerlo con XmlTextWriter, ¿cómo puedo quitar los caracteres de control específicos que no están permitidos en XML de una cadena?

código Ejemplo:

using (XmlTextWriter writer = 
    new XmlTextWriter("test.xml", Encoding.UTF8)) 
{ 
    writer.WriteStartDocument(); 
    writer.WriteStartElement("Test"); 
    writer.WriteValue("hello \xb world"); 
    writer.WriteEndElement(); 
    writer.WriteEndDocument(); 
} 

Salida:

<?xml version="1.0" encoding="utf-8"?><Test>hello &#xB; world</Test> 
+0

No se puede tener una pestaña vertical escapada en XML? ¿Podría hacer referencia al estándar? – Jodrell

+0

@Jodrell Así es, no puedes. XML es para texto, no para caracteres de control o datos binarios. http://www.w3.org/TR/REC-xml/#charsets – jasso

Respuesta

10

Esta documentación de un comportamiento está escondido en el documentation of the WriteString method pero parece que se aplica a toda la clase.

El comportamiento predeterminado de un XmlWriter creada usando Crear es lanzar una excepción ArgumentException cuando se intenta escribir los valores de caracteres en el rango 0x-0x1F (excluidos los caracteres de espacio en blanco 0x9, 0xA y 0xD). Estos caracteres XML no válidos se pueden escribir creando el XmlWriter con la propiedad CheckCharacters establecida en false. Si lo hace, resultará en los caracteres reemplazados por entidades de caracteres numéricos (&#0; hasta &#0x1F). Además, un XmlTextWriter creado con el nuevo operador reemplazará los caracteres no válidos con las entidades de caracteres numéricos de forma predeterminada.

Parece que terminas escribiendo caracteres no válidos porque estás utilizando la clase XmlTextWriter. Una mejor solución para usted sería usar el XmlWriter Class en su lugar.

+0

Es un poco extraño, pero aparentemente, aunque el constructor 'XmlTextWriter' existe, no se supone que lo uses: http: // msdn. microsoft.com/en-us/library/kkz7cs0d.aspx –

1

Construido en escapes de .NET como SecurityElement.Escape tampoco escapa/tira correctamente.

  • podría configurar CheckCharacters-false tanto en el escritor y el lector si su aplicación es la única interacción con el archivo. El archivo XML resultante aún sería técnicamente inválido sin embargo.

Ver:

XmlWriterSettings xmlWriterSettings = new XmlWriterSettings(); 
xmlWriterSettings.Encoding = new UTF8Encoding(false); 
xmlWriterSettings.CheckCharacters = false; 
var sb = new StringBuilder(); 
var w = XmlWriter.Create(sb, xmlWriterSettings); 
w.WriteStartDocument(); 
w.WriteStartElement("Test"); 
w.WriteString("hello \xb world"); 
w.WriteEndElement(); 
w.WriteEndDocument(); 
w.Close(); 
var xml = sb.ToString(); 
  • Si el ajuste CheckCharacters a true (que está por defecto) es un poco demasiado estricto, ya que simplemente lanzar una excepción un enfoque alternativo que es más indulgente con XML no válido personajes serían simplemente a despojarlos:

googlear un poco cedió la lista blanca XmlTextEncoder sin embargo, también eliminará DEL y otros en el rango U + 007F-U + 0084, U + 0086-U + 009F que de acuerdo con Valid XML Characters en wikipedia son solo válidos en ciertos contextos y que el RFC menciona como desalentado pero sigue siendo caracteres válidos.

public static class XmlTextExtentions 
{ 
    private static readonly Dictionary<char, string> textEntities = new Dictionary<char, string> { 
     { '&', "&amp;"}, { '<', "&lt;" }, { '>', "&gt;" }, 
     { '"', "&quot;" }, { '\'', "&apos;" } 
    }; 
    public static string ToValidXmlString(this string str) 
    { 
     var stripped = str 
      .Select((c,i) => new 
      { 
       c1 = c, 
       c2 = i + 1 < str.Length ? str[i+1]: default(char), 
       v = XmlConvert.IsXmlChar(c), 
       p = i + 1 < str.Length ? XmlConvert.IsXmlSurrogatePair(str[i + 1], c) : false, 
       pp = i > 0 ? XmlConvert.IsXmlSurrogatePair(c, str[i - 1]) : false 
      }) 
      .Aggregate("", (s, c) => {     
       if (c.pp) 
        return s; 
       if (textEntities.ContainsKey(c.c1)) 
        s += textEntities[c.c1]; 
       else if (c.v) 
        s += c.c1.ToString(); 
       else if (c.p) 
        s += c.c1.ToString() + c.c2.ToString(); 
       return s; 
      }); 
     return stripped; 
    } 
} 

Esto pasa todas las pruebas XmlTextEncoder a excepción de la que espera que se quiten los cuales DELXmlConvert.IsXmlChar, Wikipedia, y las marcas de especificaciones como un carácter válido (aunque desanimado).

3

acaba de encontrar esta pregunta cuando yo estaba luchando con el mismo problema y yo terminamos resolverlo con una expresión regular:

return Regex.Replace(s, @"[\u0000-\u0008\u000B\u000C\u000E-\u001F]", ""); 

espero que ayude a alguien como una solución alternativa.

Cuestiones relacionadas