XML의 텍스트 데이터를 인코딩하는 가장 좋은 방법
문제
.NET에서 XML 요소 또는 속성에서 사용하기 위해 문자열을 인코딩하는 일반적인 메소드를 찾고 있었고 즉시 찾지 못했을 때 놀랐습니다. 그래서 너무 더 나아 가기 전에 내장 기능을 놓칠 수 있습니까?
실제로 존재하지 않는다고 생각하면 내 자신의 일반적인 것을 모으고 있습니다. EncodeForXml(string data)
방법, 저는이 작업을 수행하는 가장 좋은 방법에 대해 생각하고 있습니다.
이 모든 것을 사용하는 데이터는 &, <, "등과 같은 나쁜 문자를 포함 할 수 있습니다. 때로는 제대로 탈출 한 엔티티를 포함 할 수도 있습니다. 최선의 아이디어가 아닙니다. 그것은 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
모든 사람들에게 죄송합니다. .그물
마지막으로, 우리는 여전히 내가 일하는 .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
문서화 여기
과거에는 httputility.htmlencode를 사용하여 XML의 텍스트를 인코딩했습니다. 실제로 동일한 작업을 수행합니다. 나는 아직 문제를 해결하지 못했지만 앞으로는 내가하지 않을 것이라고 말하는 것은 아닙니다. 이름에서 알 수 있듯이 XML이 아닌 HTML 용으로 만들어졌습니다.
당신은 아마 이미 그것을 읽었을 것입니다 여기에 기사가 있습니다 XML 인코딩 및 디코딩.
편집 : 물론 XMLWriter 또는 새로운 Xlement 클래스 중 하나를 사용하는 경우이 인코딩이 수행됩니다. 실제로 텍스트를 가져 와서 새 Xlement 인스턴스에 배치 한 다음 String (.toString) 버전의 요소를 반환 할 수 있습니다. 나는 그 말을 들었다 SecurityElement.escape 유틸리티 방법과 동일한 작업을 수행하지만 그것에 대해 많이 읽거나 사용하지 않았습니다.
edit2 : xlement에 대한 내 의견을 무시합니다.
마이크로 소프트 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
이 방법은 인용문과 같은 몇 가지 사항을 인코딩하지 않습니다.
SecurityElement.Escape
(WorkMAD3의 답변)는 이것으로 더 나은 일을하는 것처럼 보이며 이전 버전의 .NET에 포함되어 있습니다.
타사 코드를 신경 쓰지 않고 불법적 인 캐릭터가 XML로 만들지 않도록하는 경우 권장합니다. 마이클 크로 포트의 대답.
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>
노드 값을 읽을 때 xmlreader는 내부 텍스트의 cdata 부분을 자동으로 제거하므로 걱정할 필요가 없습니다. 유일한 캐치는 데이터를 내부 텍스트 값으로 XML 노드에 저장해야한다는 것입니다. 즉, CDATA 컨텐츠를 속성 값에 삽입 할 수 없습니다.
훌륭한! 그게 내가 말할 수있는 전부입니다.
다음은 XML을 정리하고 소독 할 업데이트 된 코드 (클래스가 아닌 함수)의 VB 변형입니다.
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>", "")
아 그리고 그것은 C#이 아닌 VB에서만 작동합니다.
취급에 대해 진지한 경우 모두 유효하지 않은 문자 중 일부는 "HTML"이 아니라 System.Xml
, 여기에 올바른 XML 인코딩을하는 가장 간단한 방법이 있습니다. 가치 데이터:
string theTextToEscape = "Something \x1d else \x1D <script>alert('123');</script>";
var x = new XmlDocument();
x.LoadXml("<r/>"); // simple, empty root element
x.DocumentElement.InnerText = theTextToEscape; // put in raw string
string escapedText = x.DocumentElement.InnerXml; // Returns: Something  else  <script>alert('123');</script>
// Repeat the last 2 lines to escape additional strings.
그것을 아는 것이 중요합니다 XmlConvert.EncodeName()
값이 아닌 엔티티/태그 이름을위한 것이기 때문에 적절하지 않습니다. 이를 사용하면 HTML-encode가 필요할 때 URL 인코딩과 같습니다.