字符串转义为XML-Attribute

时间:2010-12-16 18:29:27

标签: c# xml escaping stringbuilder xmlwriter

我查看了string escape into XML并发现它非常有用。

我想做类似的事情:转义要在XML-Attribute中使用的字符串。

该字符串可能包含\ r \ n。 XmlWriter类产生类似\ r \ n - >的东西。 &安培; #xD;&安培; #xA;

我目前使用的解决方案包括XmlWriter和StringBuilder,而且非常难看。

任何提示?

EDIT1:
抱歉让LarsH失望,买我的第一个方法是

public static string XmlEscapeAttribute(string unescaped)
{
    XmlDocument doc = new XmlDocument();
    XmlAttribute attr= doc.CreateAttribute("attr");
    attr.InnerText = unescaped;
    return attr.InnerXml;
}

它不起作用。 XmlEscapeAttribute("Foo\r\nBar")会产生"Foo\r\nBar"

我使用.NET Reflector来了解XmlTextWriter如何转义属性。它使用内部的XmlTextEncoder类...

我的方法我目前正在使用这样的方法:

public static string XmlEscapeAttribute(string unescaped)
{
    if (String.IsNullOrEmpty(unescaped)) return unescaped;

    XmlWriterSettings settings = new XmlWriterSettings();
    settings.OmitXmlDeclaration = true;
    StringBuilder sb = new StringBuilder();
    XmlWriter writer = XmlWriter.Create(sb, settings);

    writer.WriteStartElement("a");
    writer.WriteAttributeString("a", unescaped);
    writer.WriteEndElement();
    writer.Flush();
    sb.Length -= "\" />".Length;
    sb.Remove(0, "<a a=\"".Length);

    return sb.ToString();
}

这很丑陋,可能很慢,但确实有效:XmlEscapeAttribute("Foo\r\nBar")会导致"Foo&#xD;&#xA;Bar"

EDIT2:

SecurityElement.Escape(unescaped);

也不起作用。

编辑3(最终):

使用Lars的所有非常有用的评论,我的最终实现如下:

注意:有效XMl不需要.Replace("\r", "&#xD;").Replace("\n", "&#xA;");。这只是一种美容措施!

    public static string XmlEscapeAttribute(string unescaped)
    {

        XmlDocument doc = new XmlDocument();
        XmlAttribute attr= doc.CreateAttribute("attr");
        attr.InnerText = unescaped;
        // The Replace is *not* required!
        return attr.InnerXml.Replace("\r", "&#xD;").Replace("\n", "&#xA;");
    }

事实证明这是有效的XML,并且将由任何符合标准的XMl解析器进行解析:

<response message="Thank you,
LarsH!" />

3 个答案:

答案 0 :(得分:7)

修改您引用的解决方案,

public static string XmlEscape(string unescaped)
{
    XmlDocument doc = new XmlDocument();
    var node = doc.CreateAttribute("foo");
    node.InnerText = unescaped;
    return node.InnerXml;
}

我所做的只是将CreateElement()更改为CreateAttribute()。 属性节点类型确实具有InnerText和InnerXml属性。

我没有环境来测试这个,但我很想知道它是否有效。

更新:或者更简单地,使用SecurityElement.Escape() ,如您所链接问题的另一个答案所示。这将转义引号,因此适合用于属性文本。

更新2:请注意,为了使XML格式正确,回车和换行符不需要在属性值中转义。如果由于其他原因希望对它们进行转义,可以使用String.replace()进行转义,例如

SecurityElement.Escape(unescaped).Replace("\r", "&#xD;").Replace("\n", "&#xA;");

return node.InnerXml.Replace("\r", "&#xD;").Replace("\n", "&#xA;");

答案 1 :(得分:0)

public static string XmlEscapeAttribute(string unescaped)
{
    if (string.IsNullOrEmpty(unescaped))
        return unescaped;

    var attributeString = new XAttribute("n", unescaped).ToString();

    // Extract the string from the text like: n="text".
    return attributeString.Substring(3, attributeString.Length - 4);
}

此解决方案类似于@Mathias E.提出的解决方案,但是它使用LINQ to XML而不是XmlDocument,因此应该更快。

SecurityElement.Escape()解决方案有两个问题。首先,它不对新行进行编码,因此必须作为附加步骤来完成。另外,它会将撇号编码为&apos;,这在每个XML spec的属性值中是不正确的。

我的解决方案的灵感来自this post

答案 2 :(得分:-1)

如果它可以提供任何帮助,使用多种语言,可以使用createCDATASection来避免所有XML特殊字符。

它增加了这样的东西:

<tag><![CDATA[ <somecontent/> ]]></tag>