2012-10-17 12 views
7

Raccogliamo un sacco di stringhe e le inviamo ai nostri clienti in frammenti xml. Queste stringhe potrebbero contenere letteralmente qualsiasi carattere. È stato rilevato un errore causato dal tentativo di serializzare istanze XElement contenenti caratteri "non validi". Ecco un esempio:Trattare stringhe per l'inserimento in XElement

var message = new XElement("song"); 
char c = (char)0x1a; //sub 
var someData = string.Format("some{0}stuff", c); 
var attr = new XAttribute("someAttr", someData); 
message.Add(attr); 
string msgStr = message.ToString(SaveOptions.DisableFormatting); //exception here 

Il codice sopra genera un'eccezione nella riga indicata. Ecco lo stacktrace:

 
'SUB', hexadecimal value 0x1A, is an invalid character. System.ArgumentException System.ArgumentException: '', hexadecimal value 0x1A, is an invalid character. 
    at System.Xml.XmlEncodedRawTextWriter.InvalidXmlChar(Int32 ch, Char* pDst, Boolean entitize) 
    at System.Xml.XmlEncodedRawTextWriter.WriteAttributeTextBlock(Char* pSrc, Char* pSrcEnd) 
    at System.Xml.XmlEncodedRawTextWriter.WriteString(String text) 
    at System.Xml.XmlWellFormedWriter.WriteString(String text) 
    at System.Xml.XmlWriter.WriteAttributeString(String prefix, String localName, String ns, String value) 
    at System.Xml.Linq.ElementWriter.WriteStartElement(XElement e) 
    at System.Xml.Linq.ElementWriter.WriteElement(XElement e) 
    at System.Xml.Linq.XElement.WriteTo(XmlWriter writer) 
    at System.Xml.Linq.XNode.GetXmlString(SaveOptions o) 

Il mio sospetto è che questo non è il comportamento corretto e il cattivo carattere deve essere sfuggito in XML. Se questo sia desiderabile o meno è una domanda che risponderò più tardi. ci

è in qualche modo di trattare le stringhe in modo tale che questo errore potrebbe non verificarsi, o dovrei semplicemente a nudo tutti i caratteri di sotto char 0x20 e incrocio le dita:

Quindi, ecco la domanda?

+0

Buona domanda. In realtà non dovresti eliminare * tutti * i caratteri sotto 0x20, perché alcuni di essi sono debitamente escape (ad esempio CR, LF, TAB ...). Ma non riesco a vedere alcun motivo per cui gli altri non siano sfuggiti ... –

+0

I tuoi clienti avranno davvero bisogno di quei personaggi nelle corde? – climbage

+0

No. Assolutamente no. Sono resi in una casella di testo WPF o come mvcstring in una webapp. Infatti, nel nostro caso, anche le combo cr/lf/tab di @ThomasLevesque potrebbero essere eliminate perché ci aspettiamo una singola riga. Queste stringhe fanno un bel viaggio ai nostri server tramite tag idv3, software di trasmissione e server shoutcast. È possibile che le codifiche siano state violate lungo il percorso. Penso che la mia correzione sia interamente applicabile per noi. Sono ancora confuso da questa eccezione, e vorrei la conferma che sembra di aver individuato un bug in .net. – spender

risposta

5

Questo è quello che sto usando nel mio codice:

static Lazy<Regex> ControlChars = new Lazy<Regex>(() => new Regex("[\x00-\x1f]", RegexOptions.Compiled)); 

    private static string FixData_Replace(Match match) 
    { 
     if ((match.Value.Equals("\t")) || (match.Value.Equals("\n")) || (match.Value.Equals("\r"))) 
      return match.Value; 

     return "&#" + ((int)match.Value[0]).ToString("X4") + ";"; 
    } 

    public static string Fix(object data, MatchEvaluator replacer = null) 
    { 
     if (data == null) return null; 
     string fixed_data; 
     if (replacer != null) fixed_data = ControlChars.Value.Replace(data.ToString(), replacer); 
     else fixed_data = ControlChars.Value.Replace(data.ToString(), FixData_Replace); 
     return fixed_data; 
    } 

Tutti i caratteri a soffietto 0x20 (tranne \ r \ n \ t) sono sostituite dai rispettivi codici Unicode XML: 0x1f => "& # 001F" . Il parser Xml dovrebbe automaticamente rimandare indietro a 0x1f durante la lettura del file. Basta usare il nuovo XAttribute ("attribute", Fix (yourString))

Funziona con XElement e probabilmente dovrebbe funzionare anche con XAttributes.

+0

Risolto con qualcosa di simile. Per mancanza di una risposta più convincente, ti darò i punti. – spender

8

Un piccolo scavo con ILSpy ha rivelato che è possibile utilizzare il campo XmlWriter/ReaderSettings.CheckCharacters per controllare se viene lanciata un'eccezione per i caratteri non validi. Prendendo in prestito dal metodo XNode.ToString e il metodo XDocument.Parse, mi è venuta in mente i seguenti esempi:

Per un oggetto stringa i XLinq con i personaggi (di controllo) non validi:

XDocument xdoc = XDocument.Parse("<root>foo</root>"); 
using (StringWriter stringWriter = new StringWriter()) 
{ 
    XmlWriterSettings xmlWriterSettings = new XmlWriterSettings { OmitXmlDeclaration = true, CheckCharacters = false }; 
    using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, xmlWriterSettings)) 
    { 
     xdoc.WriteTo(xmlWriter); 
    } 

    return stringWriter.ToString(); 
} 

per analizzare un Oggetto XLinq con caratteri non validi:

XDocument xdoc; 
using (StringReader stringReader = new StringReader(text)) 
{ 
    XmlReaderSettings xmlReaderSettings = new XmlReaderSettings { CheckCharacters = false, DtdProcessing = DtdProcessing.Parse, MaxCharactersFromEntities = 10000000L, XmlResolver = null }; 
    using (XmlReader xmlReader = XmlReader.Create(stringReader, xmlReaderSettings)) 
    { 
     xdoc = XDocument.Load(xmlReader); 
    } 
} 
+0

Eccellente, grazie mille. –