我在.Net中寻找一个通用方法来编码一个用于Xml元素或属性的字符串,当我没有立即找到它时,我感到很惊讶.所以,在我走得太远之前,我可能只是缺少内置函数吗?
假设它确实不存在,我正在整理我自己的通用EncodeForXml(string data)
方法,我正在考虑最好的方法.
我正在使用的数据提示整个事情可能包含坏的字符,如&,<,"等.它还可能包含正确转义的实体:&,<和",这意味着只使用一个CDATA部分可能不是最好的主意.这似乎有点笨拙;我最终会得到一个很好的字符串值,可以直接在xml中使用.
我过去使用过正则表达式来捕捉坏的&符号,我想在这种情况下使用它来捕获它们以及第一步,然后对其他字符进行简单的替换.
那么,这可以进一步优化而不会太复杂,有什么我想念的吗?:
Function EncodeForXml(ByVal data As String) As String
Static badAmpersand As new Regex("&(?![a-zA-Z]{2,6};|#[0-9]{2,4};)")
data = badAmpersand.Replace(data, "&")
return data.Replace("<", "<").Replace("""", """).Replace(">", "gt;")
End Function
对不起你所有的C#-only伙计们 - 我真的不在乎我使用哪种语言,但我想让Regex保持静态,你不能在C#中做到这一点而不在方法之外声明它,所以这将是VB .净
最后,我们仍然使用.Net 2.0,但是如果有人可以将最终产品转换为字符串类的扩展方法,那也很酷.
更新前几个响应表明.Net确实有内置的方法来做到这一点.但是现在我已经开始了,我想完成我的EncodeForXml()方法只是为了它的乐趣,所以我仍然在寻找改进的想法.值得注意的是:一个更完整的字符列表应该被编码为实体(可能存储在列表/映射中),并且比在串行中的不可变字符串上执行.Replace()获得更好的性能.
根据您对输入的了解程度,您可能必须考虑到并非所有Unicode字符都是有效的XML字符.
无论Server.HtmlEncode和System.Security.SecurityElement.Escape似乎忽视了非法XML字符,而System.XML.XmlWriter.WriteString引发的ArgumentException当它遇到非法字符(除非您禁用检查在这种情况下,忽略它们).此处提供了库函数的概述.
编辑2011年8月14日:在过去的几年里,至少有几个人已经咨询了这个答案,我决定完全重写原始代码,这个代码有很多问题,包括可怕的错误处理UTF-16.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
///
/// Encodes data so that it can be safely embedded as text in XML documents.
///
public class XmlTextEncoder : TextReader {
public static string Encode(string s) {
using (var stream = new StringReader(s))
using (var encoder = new XmlTextEncoder(stream)) {
return encoder.ReadToEnd();
}
}
/// The data to be encoded in UTF-16 format.
/// It is illegal to encode certain
/// characters in XML. If true, silently omit these characters from the
/// output; if false, throw an error when encountered.
public XmlTextEncoder(TextReader source, bool filterIllegalChars=true) {
_source = source;
_filterIllegalChars = filterIllegalChars;
}
readonly Queue _buf = new Queue();
readonly bool _filterIllegalChars;
readonly TextReader _source;
public override int Peek() {
PopulateBuffer();
if (_buf.Count == 0) return -1;
return _buf.Peek();
}
public override int Read() {
PopulateBuffer();
if (_buf.Count == 0) return -1;
return _buf.Dequeue();
}
void PopulateBuffer() {
const int endSentinel = -1;
while (_buf.Count == 0 && _source.Peek() != endSentinel) {
// Strings in .NET are assumed to be UTF-16 encoded [1].
var c = (char) _source.Read();
if (Entities.ContainsKey(c)) {
// Encode all entities defined in the XML spec [2].
foreach (var i in Entities[c]) _buf.Enqueue(i);
} else if (!(0x0 <= c && c <= 0x8) &&
!new[] { 0xB, 0xC }.Contains(c) &&
!(0xE <= c && c <= 0x1F) &&
!(0x7F <= c && c <= 0x84) &&
!(0x86 <= c && c <= 0x9F) &&
!(0xD800 <= c && c <= 0xDFFF) &&
!new[] { 0xFFFE, 0xFFFF }.Contains(c)) {
// Allow if the Unicode codepoint is legal in XML [3].
_buf.Enqueue(c);
} else if (char.IsHighSurrogate(c) &&
_source.Peek() != endSentinel &&
char.IsLowSurrogate((char) _source.Peek())) {
// Allow well-formed surrogate pairs [1].
_buf.Enqueue(c);
_buf.Enqueue((char) _source.Read());
} else if (!_filterIllegalChars) {
// Note that we cannot encode illegal characters as entity
// references due to the "Legal Character" constraint of
// XML [4]. Nor are they allowed in CDATA sections [5].
throw new ArgumentException(
String.Format("Illegal character: '{0:X}'", (int) c));
}
}
}
static readonly Dictionary Entities =
new Dictionary {
{ '"', """ }, { '&', "&"}, { '\'', "'" },
{ '<', "<" }, { '>', ">" },
};
// References:
// [1] http://en.wikipedia.org/wiki/UTF-16/UCS-2
// [2] http://www.w3.org/TR/xml11/#sec-predefined-ent
// [3] http://www.w3.org/TR/xml11/#charsets
// [4] http://www.w3.org/TR/xml11/#sec-references
// [5] http://www.w3.org/TR/xml11/#sec-cdata-sect
}
可在此处找到单元测试和完整代码.
SecurityElement.Escape
记录在这里
在过去,我使用HttpUtility.HtmlEncode为xml编码文本.它真的执行相同的任务.我还没有遇到任何问题,但这并不是说我将来也不会.顾名思义,它是为HTML而不是XML而制作的.
您可能已经阅读过它,但这里有一篇关于xml编码和解码的文章.
编辑:当然,如果你使用xmlwriter或一个新的XElement类,这个编码就完成了.实际上,您可以只获取文本,将其放在新的XElement实例中,然后返回元素的字符串(.tostring)版本.我听说SecurityElement.Escape也会执行与您的实用程序方法相同的任务,但是没有阅读或使用过它.
编辑2:忽略我对XElement的评论,因为你还在使用2.0
System.Web.dll中的Microsoft AntiXss库 AntiXssEncoder类具有以下方法:
AntiXss.XmlEncode(string s) AntiXss.XmlAttributeEncode(string s)
它也有HTML:
AntiXss.HtmlEncode(string s) AntiXss.HtmlAttributeEncode(string s)
在.net 3.5+
new XText("Ito & encode this for XML").ToString();
给你:
I <want> to & encode this for XML
事实证明,这种方法不会对它应该编写的东西(如引号)进行编码.
SecurityElement.Escape
(workmad3的答案)似乎做得更好,它包含在早期版本的.net中.
如果你不介意第三方代码并且想要确保没有非法字符进入你的XML,我会推荐Michael Kropat的答案.
XmlTextWriter.WriteString()
逃跑了吗?