Ответ 1
System.XML обрабатывает кодировку для вас, поэтому вам не нужен такой метод.
Я искал универсальный метод в .Net для кодирования строки для использования в элементе или атрибуте Xml и был удивлен, когда я не нашел его сразу. Итак, прежде чем я пойду слишком много дальше, могу ли я просто пропустить встроенную функцию?
Предполагая на мгновение, что это действительно не существует, я собираю свой собственный общий метод EncodeForXml(string data)
, и я думаю о том, как это сделать.
Данные, которые я использую, которые вызвали все это, могут содержать плохие символы, такие как &, <, ", и т.д. Он также может иногда содержать правильно экранированные объекты: &, & lt;, и", что означает, что просто использование секции CDATA может быть не лучшей идеей. Это похоже на klunky anyay; Я бы скорее получил хорошее строковое значение, которое можно использовать непосредственно в 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
Извините за все, что вы С# - только люди - мне все равно, какой язык я использую, но я хочу сделать статическое Regex, и вы не можете сделать это на С#, не объявляя его вне метода, поэтому это будет VB.Net
Наконец, мы все еще работаем на .Net 2.0, но если кто-то может взять конечный продукт и превратить его в метод расширения для класса строк, это тоже будет круто.
Обновление. Первые несколько ответов показывают, что .Net действительно имеет встроенные способы сделать это. Но теперь, когда я начал, я как бы хочу закончить свой метод EncodeForXml() только ради удовольствия, поэтому я все еще ищу идеи для улучшения. Примечательно: более полный список символов, которые должны быть закодированы как сущности (возможно, сохранены в списке/карте) и что-то, что имеет лучшую производительность, чем выполнение .Replace() в неизменяемых строках в последовательном интерфейсе.
System.XML обрабатывает кодировку для вас, поэтому вам не нужен такой метод.
В зависимости от того, насколько вы знаете о вводе, вам, возможно, придется учитывать, что не все символы Юникода являются действительными символами 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;
/// <summary>
/// Encodes data so that it can be safely embedded as text in XML documents.
/// </summary>
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();
}
}
/// <param name="source">The data to be encoded in UTF-16 format.</param>
/// <param name="filterIllegalChars">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.</param>
public XmlTextEncoder(TextReader source, bool filterIllegalChars=true) {
_source = source;
_filterIllegalChars = filterIllegalChars;
}
readonly Queue<char> _buf = new Queue<char>();
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<char,string> Entities =
new Dictionary<char,string> {
{ '"', """ }, { '&', "&"}, { '\'', "'" },
{ '<', "<" }, { '>', ">" },
};
// 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
documented здесь
В прошлом я использовал HttpUtility.HtmlEncode для кодирования текста для xml. На самом деле он выполняет ту же задачу. Я еще не столкнулся с какими-либо проблемами, но это не значит, что я не буду в будущем. Как следует из названия, оно было сделано для HTML, а не для XML.
Возможно, вы уже прочитали его, но вот статья о кодировании и декодировании xml.
EDIT: Конечно, если вы используете xmlwriter или один из новых классов XElement, эта кодировка выполняется для вас. Фактически, вы можете просто взять текст, поместить его в новый экземпляр XElement, а затем вернуть строку (.tostring) версию элемента. Я слышал, что SecurityElement.Escape будет выполнять ту же задачу, что и ваш служебный метод, но havent много читал об этом или использовал его.
EDIT2: проигнорируйте мой комментарий о XElement, так как вы все еще на 2.0
Microsoft библиотека AntiXss Класс AntiXssEncoder в System.Web.dll есть методы для этого:
AntiXss.XmlEncode(string s)
AntiXss.XmlAttributeEncode(string s)
у него также есть HTML:
AntiXss.HtmlEncode(string s)
AntiXss.HtmlAttributeEncode(string s)
В .net 3.5 +
new XText("I <want> to & encode this for XML").ToString();
Дает вам:
I <want> to & encode this for XML
Забастовкa >
Оказывается, этот метод не кодирует некоторые вещи, которые он должен (например, кавычки).
SecurityElement.Escape
(ответ workmad3), похоже, лучше справляется с этим и входит в более ранние версии .net.
Если вы не возражаете против стороннего кода и хотите, чтобы никакие незаконные символы не попали в ваш XML, я бы рекомендовал ответить Michael Kropat.
XmlTextWriter.WriteString()
выполняется экранирование.
Если это приложение ASP.NET, почему бы не использовать Server.HtmlEncode()?
Это может быть так, когда вы могли бы воспользоваться методом WriteCData.
public override void WriteCData(string text)
Member of System.Xml.XmlTextWriter
Summary:
Writes out a <![CDATA[...]]> block containing the specified text.
Parameters:
text: Text to place inside the CDATA block.
Простой пример будет выглядеть следующим образом:
writer.WriteStartElement("name");
writer.WriteCData("<unsafe characters>");
writer.WriteFullEndElement();
Результат выглядит так:
<name><![CDATA[<unsafe characters>]]></name>
При чтении значений node XMLReader автоматически удаляет часть CData внутреннего текста, поэтому вам не нужно беспокоиться об этом. Единственный улов - хранить данные как значение innerText для XML node. Другими словами, вы не можете вставлять содержимое CData в значение атрибута.
Brilliant! Это все, что я могу сказать.
Вот вариант VB обновленного кода (не в классе, просто функция), который очистит, а также дезинформирует xml
Function cXML(ByVal _buf As String) As String
Dim textOut As New StringBuilder
Dim c As Char
If _buf.Trim Is Nothing OrElse _buf = String.Empty Then Return String.Empty
For i As Integer = 0 To _buf.Length - 1
c = _buf(i)
If Entities.ContainsKey(c) Then
textOut.Append(Entities.Item(c))
ElseIf (AscW(c) = &H9 OrElse AscW(c) = &HA OrElse AscW(c) = &HD) OrElse ((AscW(c) >= &H20) AndAlso (AscW(c) <= &HD7FF)) _
OrElse ((AscW(c) >= &HE000) AndAlso (AscW(c) <= &HFFFD)) OrElse ((AscW(c) >= &H10000) AndAlso (AscW(c) <= &H10FFFF)) Then
textOut.Append(c)
End If
Next
Return textOut.ToString
End Function
Shared ReadOnly Entities As New Dictionary(Of Char, String)() From {{""""c, """}, {"&"c, "&"}, {"'"c, "'"}, {"<"c, "<"}, {">"c, ">"}}
Вы можете использовать встроенный класс XAttribute, который автоматически обрабатывает кодировку:
using System.Xml.Linq;
XDocument doc = new XDocument();
List<XAttribute> attributes = new List<XAttribute>();
attributes.Add(new XAttribute("key1", "val1&val11"));
attributes.Add(new XAttribute("key2", "val2"));
XElement elem = new XElement("test", attributes.ToArray());
doc.Add(elem);
string xmlStr = doc.ToString();
Вот однолинейное решение, использующее XElements. Я использую его в очень маленьком инструменте. Мне это не нужно во второй раз, поэтому я держу его таким образом. (Его странный дуг)
StrVal = (<x a=<%= StrVal %>>END</x>).ToString().Replace("<x a=""", "").Replace(">END</x>", "")
О, и он работает только в VB не в С#