Comment supprimer des caractères hexadécimaux non valides d’une source de données XML avant de construire un XmlReader ou un XPathDocument utilisant les données?

Existe-t-il un moyen simple / général de nettoyer une source de données XML avant de l’utiliser dans un XmlReader afin que je puisse consumr correctement des données XML non conformes aux ressortingctions de caractères hexadécimaux placées sur XML?

Remarque:

  • La solution doit gérer les sources de données XML qui utilisent des codages de caractères autres que UTF-8, par exemple en spécifiant le codage des caractères lors de la déclaration du document XML. Ne pas modifier l’encodage des caractères de la source tout en supprimant les caractères hexadécimaux non valides constitue un sharepoint blocage majeur.
  • La suppression des caractères hexadécimaux non valides ne doit supprimer que les valeurs hexadécimales, car vous pouvez souvent trouver des valeurs href dans les données qui contiennent une chaîne qui correspond à une chaîne pour un caractère hexadécimal.

Contexte:

Je dois utiliser une source de données XML conforme à un format spécifique (think Atom ou stream RSS), mais je souhaite pouvoir utiliser des sources de données publiées contenant des caractères hexadécimaux non valides selon la spécification XML.

Dans .NET si vous avez un stream qui représente la source de données XML et que vous essayez ensuite de l’parsingr à l’aide d’un XmlReader et / ou XPathDocument, une exception est déclenchée en raison de l’inclusion de caractères hexadécimaux non valides dans les données XML. Ma tentative actuelle de résoudre ce problème consiste à parsingr le Stream en tant que chaîne et à utiliser une expression régulière pour supprimer et / ou remplacer les caractères hexadécimaux non valides, mais je recherche une solution plus performante.

Ce n’est peut-être pas parfait . Vous pouvez ajuster pour utiliser avec un stream.

///  /// Removes control characters and other non-UTF-8 characters ///  /// The ssortingng to process /// A ssortingng with no control characters or entities above 0x00FD public static ssortingng RemoveTroublesomeCharacters(ssortingng inSsortingng) { if (inSsortingng == null) return null; SsortingngBuilder newSsortingng = new SsortingngBuilder(); 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 { newSsortingng.Append(ch); } } return newSsortingng.ToSsortingng(); } 

J’aime le concept de liste blanche d’Eugene. Je devais faire une chose similaire à l’affiche originale, mais j’avais besoin de supporter tous les caractères Unicode, pas seulement jusqu’à 0x00FD. La spécification XML est:

Char = # x9 | #xA | #xD | [# x20- # xD7FF] | [# xE000- # xFFFD] | [# x10000- # x10FFFF]

Dans .NET, la représentation interne des caractères Unicode n’est que de 16 bits, nous ne pouvons donc pas autoriser explicitement 0x10000-0x10FFFF. La spécification XML interdit explicitement l’apparition des points de code de substitution commençant à 0xD800. Cependant, il est possible que si nous autorisons ces points de code de substitution dans notre liste blanche, le codage utf-8 de notre chaîne produirait du XML valide à la fin du codage utf-8 à partir des paires de caractères utf-16 du Chaîne .NET. Je n’ai pas exploré cela, alors je suis allé avec le pari plus sûr et je n’ai pas autorisé les substituts dans ma liste blanche.

Les commentaires dans la solution d’Eugene sont trompeurs cependant, le problème est que les caractères que nous excluons ne sont pas valides en XML … ce sont des points de code Unicode parfaitement valides. Nous ne supprimons pas les “caractères non-utf-8”. Nous supprimons les caractères utf-8 qui peuvent ne pas apparaître dans des documents XML bien formés.

 public static ssortingng XmlCharacterWhitelist( ssortingng in_ssortingng ) { if( in_ssortingng == null ) return null; SsortingngBuilder sbOutput = new SsortingngBuilder(); 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(); } 

Comme moyen de supprimer les caractères XML non valides, je vous suggère d’utiliser la méthode XmlConvert.IsXmlChar . Il a été ajouté depuis .NET Framework 4 et est également présenté dans Silverlight. Voici le petit échantillon:

 void Main() { ssortingng content = "\v\f\0"; Console.WriteLine(IsValidXmlSsortingng(content)); // False content = RemoveInvalidXmlChars(content); Console.WriteLine(IsValidXmlSsortingng(content)); // True } static ssortingng RemoveInvalidXmlChars(ssortingng text) { char[] validXmlChars = text.Where(ch => XmlConvert.IsXmlChar(ch)).ToArray(); return new ssortingng(validXmlChars); } static bool IsValidXmlSsortingng(ssortingng text) { try { XmlConvert.VerifyXmlChars(text); return true; } catch { return false; } } 

Implémentation DRY de la solution de cette réponse (en utilisant un constructeur différent – n’hésitez pas à utiliser celui dont vous avez besoin dans votre application):

 public class InvalidXmlCharacterReplacingStreamReader : StreamReader { private readonly char _replacementCharacter; public InvalidXmlCharacterReplacingStreamReader(ssortingng 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; } } 

En modernisant la réponse de Dnewcombe , vous pourriez adopter une approche légèrement plus simple.

 public static ssortingng RemoveInvalidXmlChars(ssortingng 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)); } 

ou, avec Linq

 public static ssortingng RemoveInvalidXmlChars(ssortingng input) { return new ssortingng(input.Where(value => (value >= 0x0020 && value <= 0xD7FF) || (value >= 0xE000 && value <= 0xFFFD) || value == 0x0009 || value == 0x000A || value == 0x000D).ToArray()); } 

Je serais intéressé de savoir comment les performances de ces méthodes se comparent et comment elles se comparent toutes à une approche de liste noire utilisant Buffer.BlockCopy .

Voici la réponse de dnewcome dans un StreamReader personnalisé. Il encapsule simplement un lecteur de stream réel et remplace les caractères au fur et à mesure de leur lecture.

Je n’ai mis en œuvre que quelques méthodes pour gagner du temps. Je l’ai utilisé en conjonction avec XDocument.Load et un stream de fichiers et seule la méthode Read (char [] buffer, int index, int count) a été appelée. Vous devrez peut-être implémenter des méthodes supplémentaires pour que cela fonctionne pour votre application. J’ai utilisé cette approche car elle semble plus efficace que les autres réponses. J’ai également implémenté seulement l’un des constructeurs, vous pourriez évidemment implémenter n’importe lequel des constructeurs StreamReader dont vous avez besoin, car c’est juste un passage.

J’ai choisi de remplacer les caractères plutôt que de les supprimer car cela simplifie grandement la solution. De cette manière, la longueur du texte rest la même, il n’est donc pas nécessaire de suivre un index séparé.

 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 ssortingng ReadLine() { throw new NotImplementedException(); } public override Task ReadLineAsync() { throw new NotImplementedException(); } public override ssortingng ReadToEnd() { throw new NotImplementedException(); } public override Task ReadToEndAsync() { throw new NotImplementedException(); } public override ssortingng ToSsortingng() { return implementingStreamReader.ToSsortingng(); } } 

Approche basée sur les regex

 public static ssortingng SsortingpInvalidXmlCharacters(ssortingng str) { var invalidXmlCharactersRegex = new Regex("[^\u0009\u000a\u000d\u0020-\ud7ff\ue000-\ufffd]|([\ud800-\udbff](?![\udc00-\udfff]))|((? 

}

Voir mon blog pour plus de détails

Les solutions ci-dessus semblent être destinées à supprimer les caractères non valides avant la conversion au format XML.

Utilisez ce code pour supprimer les caractères XML non valides d’une chaîne XML. par exemple. & x1A;

  public static ssortingng CleanInvalidXmlChars( ssortingng Xml, ssortingng XMLVersion ) { ssortingng pattern = Ssortingng.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, Ssortingng.Empty ); return Xml; } 

http://balajiramesh.wordpress.com/2008/05/30/ssortingp-illegal-xml-characters-based-on-w3c-standard/

Réponse modifiée ou réponse originale de Neolisk ci-dessus .
Modifications: de caractère \ 0 est passé, la suppression est effectuée, plutôt qu’un remplacement. également, fait usage de la méthode 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(ssortingng 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); } } 

Utilisez cette fonction pour supprimer les caractères XML non valides.

 public static ssortingng CleanInvalidXmlChars(ssortingng text) { ssortingng re = @"[^\x09\x0A\x0D\x20-\xD7FF\xE000-\xFFFD\x10000-x10FFFF]"; return Regex.Replace(text, re, ""); } 
 private static Ssortingng removeNonUtf8CompliantCharacters( final Ssortingng inSsortingng ) { if (null == inSsortingng ) return null; byte[] byteArr = inSsortingng.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 ); } 

Vous pouvez passer des caractères non UTF avec les éléments suivants:

 ssortingng sFinalSsortingng = ""; ssortingng hex = ""; foreach (char ch in UTFCHAR) { int tmp = ch; if ((ch < 0x00FD && ch > 0x001F) || ch == '\t' || ch == '\n' || ch == '\r') { sFinalSsortingng += ch; } else { sFinalSsortingng += "&#" + tmp+";"; } } 

Essayez ceci pour PHP!

 $goodUTF8 = iconv("utf-8", "utf-8//IGNORE", $badUTF8);