2011-11-24 9 views
13

.NET XmlTextWriter crea file xml non validi.XmlTextWriter scrive caratteri di controllo in modo errato

In XML, sono consentiti alcuni caratteri di controllo, come "scheda orizzontale" (	), ma altri no, come "scheda verticale" (). (Vedere spec.)

Ho una stringa che contiene un carattere di controllo UTF-8 che non è consentito in XML.
Anche se XmlTextWriter scappa il carattere, l'XML risultante è naturalmente non valido.

Come posso essere sicuro che XmlTextWriter non produca mai un file XML illegale?

Oppure, se non è possibile farlo con XmlTextWriter, come è possibile rimuovere i caratteri di controllo specifici non consentiti in XML da una stringa?

codice Esempio:

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

uscita:

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

Non è possibile avere una scheda verticale con escape in XML? Potresti fare riferimento allo standard? – Jodrell

+0

@Jodrell Proprio così, non puoi. XML è per testo, non per caratteri di controllo o dati binari. http://www.w3.org/TR/REC-xml/#charsets – jasso

risposta

10

Questa documentazione di un comportamento è nascosto nel documentation of the WriteString method ma suona come si applica a tutta la classe.

Il comportamento di default di un XmlWriter creato utilizzando Create è quello di gettare un ArgumentException durante il tentativo di scrivere i valori di carattere nel gamma 0x-0x1F (esclusi i caratteri di spazio bianco 0x9, 0xA e 0xD). Questi caratteri XML non validi possono essere scritti creando XmlWriter con la proprietà CheckCharacters impostata su false. Ciò comporterà nei caratteri da sostituire con entità di carattere numerico (&#0; tramite &#0x1F). Inoltre, un XmlTextWriter creato con il nuovo operatore sostituirà i caratteri non validi con il carattere numerico entità per impostazione predefinita.

Quindi sembra che si finisca di scrivere caratteri non validi perché si sta utilizzando la classe XmlTextWriter. Una soluzione migliore per te sarebbe utilizzare lo XmlWriter Class.

+0

È un po 'strano, ma a quanto pare anche se esiste il costruttore 'XmlTextWriter', non si dovrebbe utilizzarlo: http: // msdn. microsoft.com/en-us/library/kkz7cs0d.aspx –

1

Gli escreati .NET come SecurityElement.Escape non escono/striscia correttamente.

  • Si potrebbe impostare CheckCharacters-false sia lo scrittore e il lettore se l'applicazione è l'unico che interagisce con il file. Il file XML risultante sarebbe comunque tecnicamente valido.

See:

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(); 
  • Se l'impostazione CheckCharacters a true (che lo è di default) è un po 'troppo severo dal momento che sarà semplicemente un'eccezione un approccio alternativo che è più indulgente di XML non valido personaggi sarebbero a loro striscia solo:

Googling un po 'ceduto whitelist XmlTextEncoder però sarà anche rimuovere DEL e altri nell'intervallo U + 007F-U + 0084, U + 0086-U + 009F che in base a Valid XML Characters su wikipedia sono validi solo in determinati contesti e che la RFC menziona come caratteri scoraggiati ma ancora validi.

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; 
    } 
} 

Questo supera tutti i test XmlTextEncoder tranne quello che si aspetta che striscia DEL che XmlConvert.IsXmlChar, Wikipedia, ei segni spec come un carattere valido (anche se scoraggiato).

3

appena trovato questa domanda quando ero alle prese con lo stesso problema e ho finito per risolverlo con un'espressione regolare:

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

Speranza che aiuta qualcuno come una soluzione alternativa.