处理字符串以插入XElement

问题描述:

我们收集大量字符串并将它们以xml片段发送给我们的客户端。这些字符串可以包含字面上的任何字符。我们发现尝试序列化包含“坏”字符的XElement实例导致的错误。这里有一个例子:处理字符串以插入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 

上面的代码在指定的行生成一个异常。这里的堆栈跟踪:

 
'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) 

我怀疑这是不正确的行为,坏的字符应该转义成XML。这是否可取是我稍后会回答的一个问题。

所以这里的问题:

是否有处理字符串,例如,可能不会出现此错误的一些方法,或者我应该简单地剥离低于焦炭0x20所有字符,穿过我的手指?

+0

好问题。实际上,你不应该去除0x20以下的所有*字符,因为它们中的一些被正确地转义了(例如CR,LF,TAB ...)。但我看不到任何其他人没有逃脱的原因...... –

+0

您的客户是否真的需要这些字符串中的字符? – climbage

+0

不,绝对不是。它们要么呈现给WPF文本框,要么呈现为Web应用程序中的mvcstring。事实上,在我们的例子中,甚至@ThomasLevesque的cr/lf/tab组合可以被剥离,因为我们期待着一条线。这些字符串通过idv3标签,广播软件和shoutcast服务器进入我们的服务器。编码在编码过程中很可能已经被破坏。我认为我的修补程序完全适用于我们。我仍然对这个例外感到困惑,并希望确认我似乎发现了.net中的一个错误。 – spender

这是我在用我的代码:

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

所有字符波纹管为0x20(除\ r \ n \ t)将通过XML的unicode码被替换:为0x1F => “&#001F” 。 当读取文件时,Xml解析器应该自动将其还原为0x1f。 只需使用新的XAttribute(“属性”,Fix(yourString))

它适用于XElement内容,它可能也适用于XAttributes。

+0

用类似的东西修复。由于缺乏更有说服力的答案,我会给你提出观点。 – spender

用ILSpy稍微挖掘一下,发现可以使用XmlWriter/ReaderSettings.CheckCharacters字段来控制是否抛出无效字符的异常。从XNode.ToString方法和XDocument.Parse方法借用,我已经想出以下的实施例:

要字符串化具有无效(对照)字符的XLINQ对象:

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

来解析带有无效字符的XLinq对象:

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

非常好,非常感谢。 –