2012-10-17 15 views
7

Zbieramy wiele ciągów i wysyłamy je do naszych klientów w fragmentach xml. Łańcuchy te mogą zawierać dosłownie dowolną postać. Pojawił się błąd spowodowany przez serializację wystąpień XElement, które zawierają "złe" znaki. Oto przykład:Obróbka ciągów do wstawienia do 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 

Kod powyżej wytwarza wyjątek przy wskazanym linią. Oto 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) 

Podejrzewam, że nie jest to poprawne zachowanie i złe char należy uciekł do XML. Czy jest to pożądane, czy nie, jest pytanie, na które odpowiem później.

Więc oto pytanie:

Czy jest jakiś sposób leczenia sznurki tak, że ten błąd może nie wystąpić, czy mam po prostu rozebrać wszystkie znaki poniżej char 0x20 i krzyż palcami?

+0

Dobre pytanie. Właściwie nie powinieneś rozbierać * wszystkich * znaków poniżej 0x20, ponieważ niektóre z nich są odpowiednio chronione (np. CR, LF, TAB ...). Ale nie widzę żadnego powodu, dla którego inni nie uciekną ... –

+0

Czy twoi klienci kiedykolwiek będą potrzebować tych znaków w strunach? – climbage

+0

Nie. Zdecydowanie nie. Są one renderowane do pola tekstowego WPF lub jako mvcstring w aplikacji internetowej. W rzeczywistości w naszym przypadku nawet kombinacje cr/lf/tab @ThomasLevesque mogą zostać usunięte, ponieważ oczekujemy jednej linii. Te ciągi stanowią dość podróż do naszych serwerów za pomocą tagów idv3, oprogramowania do emisji i serwerów shoutcast. Jest całkiem możliwe, że kodowanie zostało zmanipulowane po drodze. Uważam, że moja poprawka jest całkowicie dla nas odpowiednia. Nadal jestem zdezorientowany tym wyjątkiem i chciałbym potwierdzić, że prawdopodobnie zauważyłem błąd w .net. – spender

Odpowiedz

5

To właśnie używam w moim kodu:

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

Wszystkie znaki mieszkowe 0x20 (z wyjątkiem \ r \ n \ t) są zastępowane przez ich kody Unicode XML: 0x1F => "& # 001f" . Analizator składni XML powinien automatycznie cofnąć go do 0x1f podczas odczytu pliku. Po prostu użyj nowego XAttribute ("atrybut", Fix (yourString))

Działa dla zawartości XElement a prawdopodobnie powinien również działać dla XAttributes.

+0

Naprawiono za pomocą czegoś podobnego. Z braku bardziej przekonującej odpowiedzi dam ci punkty. – spender

8

Nieco kopania z ILSpy wykazały, że można użyć pola XmlWriter/ReaderSettings.CheckCharacters do kontroli, czy wyjątek jest rzucony dla nieprawidłowych znaków. Pożyczanie od sposobu XNode.ToString i sposobu XDocument.Parse, mam wymyślić poniższych przykładach:

Aby stringify obiekt XLinq z nieprawidłowych (kontrola) znaków:

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

Do analizowania Obiekt XLinq z nieprawidłowymi znakami:

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

Doskonale, wielkie dzięki. –