Gibt es eine einfache / allgemeine Möglichkeit, eine XML-basierte Datenquelle vor der Verwendung in einem XmlReader zu bereinigen, damit ich XML-Daten ordnungsgemäß verwenden kann, die nicht den für XML geltenden hexadezimalen Zeichenbeschränkungen entsprechen?
Hinweis:
- Die Lösung muss XML-Datenquellen verarbeiten, die andere Zeichencodierungen als UTF-8 verwenden, z. B. indem die Zeichencodierung in der XML-Dokumentdeklaration angegeben wird. Es war ein wichtiger Knackpunkt, die Zeichenkodierung der Quelle nicht zu entstellen, während ungültige hexadezimale Zeichen entfernt wurden.
- Das Entfernen ungültiger hexadezimaler Zeichen sollte nur hexadezimal codierte Werte entfernen, da Sie häufig href-Werte in Daten finden können, die zufällig eine Zeichenfolge enthalten, die einer Zeichenfolgenübereinstimmung für ein hexadezimales Zeichen entspricht.
Hintergrund:
Ich muss eine XML-basierte Datenquelle verwenden, die einem bestimmten Format entspricht (z. B. Atom- oder RSS-Feeds), möchte jedoch veröffentlichte Datenquellen verwenden können, die ungültige Hexadezimalzeichen gemäß der XML-Spezifikation enthalten.
Wenn Sie in .NET einen Stream haben, der die XML-Datenquelle darstellt, und dann versuchen, ihn mit einem XmlReader und / oder XPathDocument zu analysieren, wird eine Ausnahme ausgelöst, da ungültige Hexadezimalzeichen in die XML-Daten aufgenommen werden. Mein aktueller Versuch, dieses Problem zu beheben, besteht darin, den Stream als Zeichenfolge zu analysieren und einen regulären Ausdruck zu verwenden, um die ungültigen hexadezimalen Zeichen zu entfernen und / oder zu ersetzen. Ich suche jedoch nach einer leistungsfähigeren Lösung.
quelle
Ich mag Eugenes Whitelist-Konzept. Ich musste etwas Ähnliches tun wie das Originalposter, aber ich musste alle Unicode-Zeichen unterstützen, nicht nur bis zu 0x00FD. Die XML-Spezifikation lautet:
Char = # x9 | #xA | #xD | [# x20- # xD7FF] | [# xE000- # xFFFD] | [# x10000- # x10FFFF]
In .NET beträgt die interne Darstellung von Unicode-Zeichen nur 16 Bit, daher können wir 0x10000-0x10FFFF nicht explizit zulassen. Die XML-Spezifikation verhindert ausdrücklich , dass die Ersatzcodepunkte ab 0xD800 angezeigt werden. Es ist jedoch möglich, dass, wenn wir diese Ersatzcodepunkte in unserer Whitelist zulassen, die utf-8-Codierung unserer Zeichenfolge am Ende gültiges XML erzeugt, solange die richtige utf-8-Codierung aus den Ersatzpaaren von utf-16-Zeichen in der Liste erstellt wurde .NET-Zeichenfolge. Ich habe dies jedoch nicht untersucht, also habe ich mich für die sicherere Wette entschieden und die Leihmütter in meiner Whitelist nicht zugelassen.
Die Kommentare in Eugenes Lösung sind jedoch irreführend. Das Problem ist, dass die Zeichen, die wir ausschließen, in XML nicht gültig sind. Sie sind perfekt gültige Unicode-Codepunkte. Wir entfernen keine "Nicht-Utf-8-Zeichen". Wir entfernen utf-8-Zeichen, die möglicherweise nicht in wohlgeformten XML-Dokumenten enthalten sind.
public static string XmlCharacterWhitelist( string in_string ) { if( in_string == null ) return null; StringBuilder sbOutput = new StringBuilder(); char ch; for( int i = 0; i < in_string.Length; i++ ) { ch = in_string[i]; if( ( ch >= 0x0020 && ch <= 0xD7FF ) || ( ch >= 0xE000 && ch <= 0xFFFD ) || ch == 0x0009 || ch == 0x000A || ch == 0x000D ) { sbOutput.Append( ch ); } } return sbOutput.ToString(); }
quelle
doc = XDocument.Load(@strXMLPath);
zu einer AusnahmeUm ungültige XML-Zeichen zu entfernen, empfehle ich die Verwendung der XmlConvert.IsXmlChar- Methode. Es wurde seit .NET Framework 4 hinzugefügt und wird auch in Silverlight dargestellt. Hier ist das kleine Beispiel:
void Main() { string content = "\v\f\0"; Console.WriteLine(IsValidXmlString(content)); // False content = RemoveInvalidXmlChars(content); Console.WriteLine(IsValidXmlString(content)); // True } static string RemoveInvalidXmlChars(string text) { char[] validXmlChars = text.Where(ch => XmlConvert.IsXmlChar(ch)).ToArray(); return new string(validXmlChars); } static bool IsValidXmlString(string text) { try { XmlConvert.VerifyXmlChars(text); return true; } catch { return false; } }
quelle
DRY-Implementierung der Lösung dieser Antwort (unter Verwendung eines anderen Konstruktors - Sie können auch den in Ihrer Anwendung benötigten verwenden):
public class InvalidXmlCharacterReplacingStreamReader : StreamReader { private readonly char _replacementCharacter; public InvalidXmlCharacterReplacingStreamReader(string fileName, char replacementCharacter) : base(fileName) { this._replacementCharacter = replacementCharacter; } public override int Peek() { int ch = base.Peek(); if (ch != -1 && IsInvalidChar(ch)) { return this._replacementCharacter; } return ch; } public override int Read() { int ch = base.Read(); if (ch != -1 && IsInvalidChar(ch)) { return this._replacementCharacter; } return ch; } public override int Read(char[] buffer, int index, int count) { int readCount = base.Read(buffer, index, count); for (int i = index; i < readCount + index; i++) { char ch = buffer[i]; if (IsInvalidChar(ch)) { buffer[i] = this._replacementCharacter; } } return readCount; } private static bool IsInvalidChar(int ch) { return (ch < 0x0020 || ch > 0xD7FF) && (ch < 0xE000 || ch > 0xFFFD) && ch != 0x0009 && ch != 0x000A && ch != 0x000D; } }
quelle
Wenn Sie die Antwort von dnewcombe modernisieren , können Sie einen etwas einfacheren Ansatz wählen
public static string RemoveInvalidXmlChars(string input) { var isValid = new Predicate<char>(value => (value >= 0x0020 && value <= 0xD7FF) || (value >= 0xE000 && value <= 0xFFFD) || value == 0x0009 || value == 0x000A || value == 0x000D); return new string(Array.FindAll(input.ToCharArray(), isValid)); }
oder mit Linq
public static string RemoveInvalidXmlChars(string input) { return new string(input.Where(value => (value >= 0x0020 && value <= 0xD7FF) || (value >= 0xE000 && value <= 0xFFFD) || value == 0x0009 || value == 0x000A || value == 0x000D).ToArray()); }
Mich würde interessieren, wie die Leistung dieser Methoden verglichen wird und wie sie alle mit einem Black-List-Ansatz verglichen werden
Buffer.BlockCopy
.quelle
XmlReader.Create
anstatt die gesamte Datei in eine Zeichenfolge im Speicher zu laden.Hier ist dnewcome ‚s Antwort in einem benutzerdefinierten Stream. Es umschließt einfach einen echten Stream-Reader und ersetzt die gelesenen Zeichen.
Ich habe nur wenige Methoden implementiert, um mir Zeit zu sparen. Ich habe dies in Verbindung mit XDocument.Load und einem Dateistream verwendet und nur die Read-Methode (char [] buffer, int index, int count) wurde aufgerufen, also hat es so funktioniert. Möglicherweise müssen Sie zusätzliche Methoden implementieren, damit dies für Ihre Anwendung funktioniert. Ich habe diesen Ansatz verwendet, weil er effizienter zu sein scheint als die anderen Antworten. Ich habe auch nur einen der Konstruktoren implementiert. Sie können natürlich jeden der StreamReader-Konstruktoren implementieren, die Sie benötigen, da es sich nur um einen Durchgang handelt.
Ich habe mich dafür entschieden, die Zeichen zu ersetzen, anstatt sie zu entfernen, da dies die Lösung erheblich vereinfacht. Auf diese Weise bleibt die Länge des Textes gleich, sodass kein separater Index nachverfolgt werden muss.
public class InvalidXmlCharacterReplacingStreamReader : TextReader { private StreamReader implementingStreamReader; private char replacementCharacter; public InvalidXmlCharacterReplacingStreamReader(Stream stream, char replacementCharacter) { implementingStreamReader = new StreamReader(stream); this.replacementCharacter = replacementCharacter; } public override void Close() { implementingStreamReader.Close(); } public override ObjRef CreateObjRef(Type requestedType) { return implementingStreamReader.CreateObjRef(requestedType); } public void Dispose() { implementingStreamReader.Dispose(); } public override bool Equals(object obj) { return implementingStreamReader.Equals(obj); } public override int GetHashCode() { return implementingStreamReader.GetHashCode(); } public override object InitializeLifetimeService() { return implementingStreamReader.InitializeLifetimeService(); } public override int Peek() { int ch = implementingStreamReader.Peek(); if (ch != -1) { if ( (ch < 0x0020 || ch > 0xD7FF) && (ch < 0xE000 || ch > 0xFFFD) && ch != 0x0009 && ch != 0x000A && ch != 0x000D ) { return replacementCharacter; } } return ch; } public override int Read() { int ch = implementingStreamReader.Read(); if (ch != -1) { if ( (ch < 0x0020 || ch > 0xD7FF) && (ch < 0xE000 || ch > 0xFFFD) && ch != 0x0009 && ch != 0x000A && ch != 0x000D ) { return replacementCharacter; } } return ch; } public override int Read(char[] buffer, int index, int count) { int readCount = implementingStreamReader.Read(buffer, index, count); for (int i = index; i < readCount+index; i++) { char ch = buffer[i]; if ( (ch < 0x0020 || ch > 0xD7FF) && (ch < 0xE000 || ch > 0xFFFD) && ch != 0x0009 && ch != 0x000A && ch != 0x000D ) { buffer[i] = replacementCharacter; } } return readCount; } public override Task<int> ReadAsync(char[] buffer, int index, int count) { throw new NotImplementedException(); } public override int ReadBlock(char[] buffer, int index, int count) { throw new NotImplementedException(); } public override Task<int> ReadBlockAsync(char[] buffer, int index, int count) { throw new NotImplementedException(); } public override string ReadLine() { throw new NotImplementedException(); } public override Task<string> ReadLineAsync() { throw new NotImplementedException(); } public override string ReadToEnd() { throw new NotImplementedException(); } public override Task<string> ReadToEndAsync() { throw new NotImplementedException(); } public override string ToString() { return implementingStreamReader.ToString(); } }
quelle
Regex-basierter Ansatz
public static string StripInvalidXmlCharacters(string str) { var invalidXmlCharactersRegex = new Regex("[^\u0009\u000a\u000d\u0020-\ud7ff\ue000-\ufffd]|([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])"); return invalidXmlCharactersRegex.Replace(str, "");
}}
Weitere Details finden Sie in meinem Blogpost
quelle
Die oben genannten Lösungen scheinen zum Entfernen ungültiger Zeichen vor der Konvertierung in XML zu dienen.
Verwenden Sie diesen Code, um ungültige XML-Zeichen aus einer XML-Zeichenfolge zu entfernen. z.B. & x1A;
public static string CleanInvalidXmlChars( string Xml, string XMLVersion ) { string pattern = String.Empty; switch( XMLVersion ) { case "1.0": pattern = @"&#x((10?|[2-F])FFF[EF]|FDD[0-9A-F]|7F|8[0-46-9A-F]9[0-9A-F]);"; break; case "1.1": pattern = @"&#x((10?|[2-F])FFF[EF]|FDD[0-9A-F]|[19][0-9A-F]|7F|8[0-46-9A-F]|0?[1-8BCEF]);"; break; default: throw new Exception( "Error: Invalid XML Version!" ); } Regex regex = new Regex( pattern, RegexOptions.IgnoreCase ); if( regex.IsMatch( Xml ) ) Xml = regex.Replace( Xml, String.Empty ); return Xml; }
http://balajiramesh.wordpress.com/2008/05/30/strip-illegal-xml-characters-based-on-w3c-standard/
quelle
Geänderte Antwort oder ursprüngliche Antwort von Neolisk oben .
Änderungen: Das Zeichen \ 0 wird übergeben, das Entfernen erfolgt und nicht das Ersetzen. Außerdem wurde die Methode XmlConvert.IsXmlChar (char) verwendet
/// <summary> /// Replaces invalid Xml characters from input file, NOTE: if replacement character is \0, then invalid Xml character is removed, instead of 1-for-1 replacement /// </summary> public class InvalidXmlCharacterReplacingStreamReader : StreamReader { private readonly char _replacementCharacter; public InvalidXmlCharacterReplacingStreamReader(string fileName, char replacementCharacter) : base(fileName) { _replacementCharacter = replacementCharacter; } public override int Peek() { int ch = base.Peek(); if (ch != -1 && IsInvalidChar(ch)) { if ('\0' == _replacementCharacter) return Peek(); // peek at the next one return _replacementCharacter; } return ch; } public override int Read() { int ch = base.Read(); if (ch != -1 && IsInvalidChar(ch)) { if ('\0' == _replacementCharacter) return Read(); // read next one return _replacementCharacter; } return ch; } public override int Read(char[] buffer, int index, int count) { int readCount= 0, ch; for (int i = 0; i < count && (ch = Read()) != -1; i++) { readCount++; buffer[index + i] = (char)ch; } return readCount; } private static bool IsInvalidChar(int ch) { return !XmlConvert.IsXmlChar((char)ch); } }
quelle
Ich habe eine leicht aktualisierte Version von @ Neolisk's Antwort erstellt , die die
*Async
Funktionen unterstützt und die .Net 4.0-XmlConvert.IsXmlChar
Funktion verwendet.public class InvalidXmlCharacterReplacingStreamReader : StreamReader { private readonly char _replacementCharacter; public InvalidXmlCharacterReplacingStreamReader(string fileName, char replacementCharacter) : base(fileName) { _replacementCharacter = replacementCharacter; } public InvalidXmlCharacterReplacingStreamReader(Stream stream, char replacementCharacter) : base(stream) { _replacementCharacter = replacementCharacter; } public override int Peek() { var ch = base.Peek(); if (ch != -1 && IsInvalidChar(ch)) { return _replacementCharacter; } return ch; } public override int Read() { var ch = base.Read(); if (ch != -1 && IsInvalidChar(ch)) { return _replacementCharacter; } return ch; } public override int Read(char[] buffer, int index, int count) { var readCount = base.Read(buffer, index, count); ReplaceInBuffer(buffer, index, readCount); return readCount; } public override async Task<int> ReadAsync(char[] buffer, int index, int count) { var readCount = await base.ReadAsync(buffer, index, count).ConfigureAwait(false); ReplaceInBuffer(buffer, index, readCount); return readCount; } private void ReplaceInBuffer(char[] buffer, int index, int readCount) { for (var i = index; i < readCount + index; i++) { var ch = buffer[i]; if (IsInvalidChar(ch)) { buffer[i] = _replacementCharacter; } } } private static bool IsInvalidChar(int ch) { return IsInvalidChar((char)ch); } private static bool IsInvalidChar(char ch) { return !XmlConvert.IsXmlChar(ch); } }
quelle
Verwenden Sie diese Funktion, um ungültige XML-Zeichen zu entfernen.
public static string CleanInvalidXmlChars(string text) { string re = @"[^\x09\x0A\x0D\x20-\xD7FF\xE000-\xFFFD\x10000-x10FFFF]"; return Regex.Replace(text, re, ""); }
quelle
private static String removeNonUtf8CompliantCharacters( final String inString ) { if (null == inString ) return null; byte[] byteArr = inString.getBytes(); for ( int i=0; i < byteArr.length; i++ ) { byte ch= byteArr[i]; // remove any characters outside the valid UTF-8 range as well as all control characters // except tabs and new lines if ( !( (ch > 31 && ch < 253 ) || ch == '\t' || ch == '\n' || ch == '\r') ) { byteArr[i]=' '; } } return new String( byteArr ); }
quelle
Sie können Nicht-UTF-Zeichen wie folgt übergeben:
string sFinalString = ""; string hex = ""; foreach (char ch in UTFCHAR) { int tmp = ch; if ((ch < 0x00FD && ch > 0x001F) || ch == '\t' || ch == '\n' || ch == '\r') { sFinalString += ch; } else { sFinalString += "&#" + tmp+";"; } }
quelle

keine gültige XML-Zeichenentitätsreferenz). Es ist auch irreführend, weil es Zeichen entfernt, die sowohl in Unicode als auch in XML gültig sind.U+0001 START OF HEADING
in einem wohlgeformten XML-Dokument nicht zulässig, und selbst wenn Sie versuchen, es als zu maskieren, ist dies in einem wohlgeformten XML-Dokument
immer noch nicht zulässig.Versuchen Sie dies für PHP!
$goodUTF8 = iconv("utf-8", "utf-8//IGNORE", $badUTF8);
quelle