8

我们收集大量字符串并以 xml 片段的形式将它们发送给我们的客户。这些字符串实际上可以包含任何字符。我们一直看到尝试序列化包含“坏”字符的 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',十六进制值 0x1A,是一个无效字符。System.ArgumentException System.ArgumentException:'',十六进制值 0x1A,是无效字符。
   在 System.Xml.XmlEncodedRawTextWriter.InvalidXmlChar(Int32 ch,Char* pDst,布尔实体)
   在 System.Xml.XmlEncodedRawTextWriter.WriteAttributeTextBlock(Char* pSrc,Char* pSrcEnd)
   在 System.Xml.XmlEncodedRawTextWriter.WriteString(字符串文本)
   在 System.Xml.XmlWellFormedWriter.WriteString(字符串文本)
   在 System.Xml.XmlWriter.WriteAttributeString(字符串前缀,字符串 localName,字符串 ns,字符串值)
   在 System.Xml.Linq.ElementWriter.WriteStartElement(XElement e)
   在 System.Xml.Linq.ElementWriter.WriteElement(XElement e)
   在 System.Xml.Linq.XElement.WriteTo(XmlWriter 作家)
   在 System.Xml.Linq.XNode.GetXmlString(SaveOptions o)

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

那么问题来了:

是否有某种处理字符串的方法可能不会发生此错误,或者我应该简单地剥离 char 下方的所有字符0x20并交叉手指?

4

2 回答 2

9

对 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);
    }
}
于 2013-02-13T02:18:17.730 回答
6

这是我在我的代码中使用的:

    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 => "f"。Xml 解析器在读取文件时应自动将其转义回 0x1f。只需使用 new XAttribute("attribute", Fix(yourString))

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

于 2012-10-18T09:30:50.987 回答