Как удалить недопустимые шестнадцатеричные символы из источника данных на основе XML до создания XmlReader или XPathDocument, который использует данные?
Есть ли простой / общий способ очистки источника данных на основе XML до его использования в XmlReader, чтобы я мог изящно потреблять XML-данные, которые не соответствуют шестнадцатеричным ограничениям символов, помещенным в XML?
Заметка:
- Решение должно обрабатывать источники данных XML, которые используют кодировки символов, отличные от UTF-8, например, путем указания кодировки символов в декларации документа XML. Важным моментом является не кодирование символьного кода источника при снятии недопустимых шестнадцатеричных символов.
- Удаление недействительных шестнадцатеричных символов должно только удалять шестнадцатеричные закодированные значения, так как вы часто можете найти значения href в данных, которые происходят, содержит строку, которая будет соответствовать строке для шестнадцатеричного символа.
Задний план:
- Проверка динамически добавленных полей
- Что означает == $ 0 (double equals dollar zero) в инструментах разработчика Chrome?
- accepts_nested_attributes_ для неудачной проверки дочерней ассоциации
- Инструмент проверки XML-схемы (XSD)?
- Проверка подлинности электронной почты с помощью jQuery
Мне нужно использовать источник данных на основе XML, который соответствует определенному формату (думаю, Atom или RSS-каналы), но хочет иметь возможность использовать источники данных, которые были опубликованы, которые содержат недопустимые шестнадцатеричные символы в спецификации XML.
В .NET, если у вас есть Stream, который представляет источник данных XML, а затем попытайтесь разобрать его с помощью XmlReader и / или XPathDocument, исключение возникает из-за включения недопустимых шестнадцатеричных символов в XML-данных. Моя текущая попытка решить эту проблему состоит в том, чтобы проанализировать stream как строку и использовать регулярное выражение для удаления и / или замены недопустимых шестнадцатеричных символов, но я ищу более эффективное решение.
- Фильтр аутентификации и сервлет для входа в систему
- Spring - redirect после POST (даже с ошибками проверки)
- Проверка HTML5 перед отправкой ajax
- Как выполнить проверку в JSF, как создать пользовательский валидатор в JSF
- Как проверить два поля пароля с помощью ajax?
- ASP.NET MVC - Пользовательское сообщение проверки для типов значений
- Простой скрипт проверки формы jQuery
- jQuery: какой лучший способ ограничить «число» - только вход для текстовых полей? (разрешить десятичные точки)
Это может быть не идеально (добавлено внимание, поскольку люди не имеют этого отказа от ответственности), но то, что я сделал в этом случае, ниже. Вы можете настроить его для использования с streamом.
/// /// Removes control characters and other non-UTF-8 characters /// /// The string to process /// A string with no control characters or entities above 0x00FD public static string RemoveTroublesomeCharacters(string inString) { if (inString == null) return null; StringBuilder newString = new StringBuilder(); char ch; for (int i = 0; i < inString.Length; i++) { ch = inString[i]; // remove any characters outside the valid UTF-8 range as well as all control characters // except tabs and new lines //if ((ch < 0x00FD && ch > 0x001F) || ch == '\t' || ch == '\n' || ch == '\r') //if using .NET version prior to 4, use above logic if (XmlConvert.IsXmlChar(ch)) //this method is new in .NET 4 { newString.Append(ch); } } return newString.ToString(); }
Мне нравится концепция белого списка Юджина. Мне нужно было сделать то же самое, что и оригинальный плакат, но мне нужно было поддерживать все символы Unicode, а не только до 0x00FD. Спецификация XML:
Char = # x9 | #xA | #xD | [# x20- # xD7FF] | [# xE000- # xFFFD] | [# X10000- # x10FFFF]
В .NET внутреннее представление символов Unicode составляет всего 16 бит, поэтому мы не можем «разрешать» 0x10000-0x10FFFF явно. Спецификация XML явно запрещает появление суррогатных кодовых пунктов, начиная с 0xD800. Однако возможно, что если бы мы разрешили эти суррогатные кодовые точки в нашем белом списке, то utf-8, кодирующая нашу строку, может привести к правильному XML в конце, если бы правильная кодировка utf-8 была создана из суррогатных пар символов utf-16 в Строка .NET. Я еще не изучил это, поэтому я пошел с более безопасной ставкой и не разрешил суррогатам в моем белом списке.
Замечания в решении Юджина вводят в заблуждение, однако проблема заключается в том, что символы, которые мы исключаем, недействительны в XML … они отлично подходят для кодов Unicode. Мы не удаляем символы «не-utf-8». Мы удаляем символы utf-8, которые могут не отображаться в хорошо сформированных XML-документах.
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(); }
В качестве способа удаления недопустимых символов XML я предлагаю использовать метод XmlConvert.IsXmlChar . Он был добавлен с .NET Framework 4 и представлен также в Silverlight. Вот небольшой пример:
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; } }
СУЩЕСТВЕННАЯ реализация решения этого ответа (с использованием другого конструктора – не стесняйтесь использовать тот, который вам нужен в вашем приложении):
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; } }
Модернизировав ответ dnewcombe , вы можете сделать несколько более простой подход
public static string RemoveInvalidXmlChars(string input) { var isValid = new Predicate(value => (value >= 0x0020 && value <= 0xD7FF) || (value >= 0xE000 && value <= 0xFFFD) || value == 0x0009 || value == 0x000A || value == 0x000D); return new string(Array.FindAll(input.ToCharArray(), isValid)); }
или, с 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()); }
Мне было бы интересно узнать, как производительность этих методов сравнивается и как все они сравниваются с черным списком с использованием Buffer.BlockCopy
.
Вот ответ dnewcome в пользовательском StreamReader. Он просто обертывает читателя реального streamа и заменяет символы по мере их чтения.
Я только реализовал несколько методов, чтобы сэкономить время. Я использовал это в сочетании с XDocument.Load и streamом файлов, и был вызван только метод Read (char [] buffer, int index, int count), поэтому он работал так. Возможно, вам понадобится реализовать дополнительные методы, чтобы заставить это работать для вашего приложения. Я использовал этот подход, потому что он кажется более эффективным, чем другие ответы. Я также только реализовал один из конструкторов, вы, очевидно, могли бы реализовать любой конструктор StreamReader, который вам нужен, поскольку это просто проход.
Я решил заменить символы, а не удалять их, потому что это значительно упрощает решение. Таким образом, длина текста остается неизменной, поэтому нет необходимости отслеживать отдельный индекс.
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 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 ReadBlockAsync(char[] buffer, int index, int count) { throw new NotImplementedException(); } public override string ReadLine() { throw new NotImplementedException(); } public override Task ReadLineAsync() { throw new NotImplementedException(); } public override string ReadToEnd() { throw new NotImplementedException(); } public override Task ReadToEndAsync() { throw new NotImplementedException(); } public override string ToString() { return implementingStreamReader.ToString(); } }
Подход, основанный на регулярном выражении
public static string StripInvalidXmlCharacters(string str) { var invalidXmlCharactersRegex = new Regex("[^\u0009\u000a\u000d\u0020-\ud7ff\ue000-\ufffd]|([\ud800-\udbff](?![\udc00-\udfff]))|((?
}
См. Мой блогпост для более подробной информации
Вышеупомянутые решения, похоже, предназначены для удаления недопустимых символов до преобразования в XML.
Используйте этот код для удаления недопустимых символов XML из строки XML. например. & X1A;
public static string CleanInvalidXmlChars( string Xml, string XMLVersion ) { string pattern = String.Empty; switch( XMLVersion ) { case "1.0": pattern = @"((10?|[2-F])FFF[EF]|FDD[0-9A-F]|7F|8[0-46-9A-F]9[0-9A-F]);"; break; case "1.1": pattern = @"((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/
Модифицированный ответ или оригинальный ответ Neolisk выше .
Изменения: из символа \ 0 передается, удаление выполняется, а не замена. также, используя метод XmlConvert.IsXmlChar (char)
/// /// 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 /// 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); } }
Используйте эту функцию для удаления недопустимых символов xml.
public static string CleanInvalidXmlChars(string text) { string re = @"[^\x09\x0A\x0D\x20-\xD7FF\xE000-\xFFFD\x10000-x10FFFF]"; return Regex.Replace(text, re, ""); }
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 ); }
Вы можете передавать символы без UTF со следующим:
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+";"; } }
Попробуйте это для PHP!
$goodUTF8 = iconv("utf-8", "utf-8//IGNORE", $badUTF8);