Escape caracteres XML no válidos en C#
.net escaping (6)
Tengo una cadena que contiene caracteres XML no válidos. ¿Cómo puedo escapar (o eliminar) caracteres XML no válidos antes de analizar la cadena?
Aquí hay una versión optimizada del método anterior RemoveInvalidXmlChars que no crea una nueva matriz en cada llamada, por lo tanto, estresando el GC:
public static string RemoveInvalidXmlChars(string text)
{
if (text == null) return text;
if (text.Length == 0) return text;
// a bit complicated, but avoids memory usage if not necessary
StringBuilder result = null;
for (int i = 0; i < text.Length; i++)
{
var ch = text[i];
if (XmlConvert.IsXmlChar(ch))
{
result?.Append(ch);
}
else
{
if (result == null)
{
result = new StringBuilder();
result.Append(text.Substring(0, i));
}
}
}
if (result == null)
return text; // no invalid xml chars detected - return original text
else
return result.ToString();
}
Como la forma de eliminar los caracteres XML no válidos, le sugiero que utilice el método XmlConvert.IsXmlChar . Se agregó desde .NET Framework 4 y también se presenta en Silverlight. Aquí está la pequeña muestra:
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) {
var 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;
}
}
Y como la forma de escapar de los caracteres XML no válidos, le sugiero que use el método XmlConvert.EncodeName . Aquí está la pequeña muestra:
void Main() {
const string content = "/v/f/0";
Console.WriteLine(IsValidXmlString(content)); // False
string encoded = XmlConvert.EncodeName(content);
Console.WriteLine(IsValidXmlString(encoded)); // True
string decoded = XmlConvert.DecodeName(encoded);
Console.WriteLine(content == decoded); // True
}
static bool IsValidXmlString(string text) {
try {
XmlConvert.VerifyXmlChars(text);
return true;
} catch {
return false;
}
}
Actualización: debe mencionarse que la operación de codificación produce una cadena con una longitud que es mayor o igual que la longitud de una cadena fuente. Puede ser importante cuando almacena una cadena codificada en una base de datos en una columna de cadena con limitación de longitud y valida la longitud de la cadena de fuente en su aplicación para que se ajuste a la limitación de la columna de datos.
El método RemoveInvalidXmlChars proporcionado por Irishman no admite caracteres sustitutos. Para probarlo, use el siguiente ejemplo:
static void Main()
{
const string content = "/v/U00010330";
string newContent = RemoveInvalidXmlChars(content);
Console.WriteLine(newContent);
}
Esto devuelve una cadena vacía, ¡pero no debería! Debería devolver "/ U00010330" porque el carácter U+10330 es un carácter XML válido.
Para admitir personajes sustitutos, sugiero usar el siguiente método:
public static string RemoveInvalidXmlChars(string text)
{
if (string.IsNullOrEmpty(text))
return text;
int length = text.Length;
StringBuilder stringBuilder = new StringBuilder(length);
for (int i = 0; i < length; ++i)
{
if (XmlConvert.IsXmlChar(text[i]))
{
stringBuilder.Append(text[i]);
}
else if (i + 1 < length && XmlConvert.IsXmlSurrogatePair(text[i + 1], text[i]))
{
stringBuilder.Append(text[i]);
stringBuilder.Append(text[i + 1]);
++i;
}
}
return stringBuilder.ToString();
}
Si está escribiendo xml, simplemente use las clases provistas por el framework para crear el xml. No tendrás que molestarte en escapar ni nada.
Console.Write(new XElement("Data", "< > &"));
Producirá
<Data>< > &</Data>
Si necesita leer un archivo XML que está mal formado, no use expresiones regulares. En su lugar, use el Html Agility Pack .
using System;
using System.Security;
class Sample {
static void Main() {
string text = "Escape characters : < > & /" /'";
string xmlText = SecurityElement.Escape(text);
//output:
//Escape characters : < > & " '
Console.WriteLine(xmlText);
}
}
// Replace invalid characters with empty strings.
Regex.Replace(inputString, @"[^/w/.@-]", "");
El patrón de expresión regular [^ / w. @ -] coincide con cualquier carácter que no sea un carácter de palabra, un punto, un símbolo @ o un guión. Un carácter de palabra es cualquier letra, dígito decimal o conector de puntuación, como un guión bajo. Cualquier carácter que coincida con este patrón se reemplaza por String.Empty, que es la cadena definida por el patrón de reemplazo. Para permitir caracteres adicionales en la entrada del usuario, agregue esos caracteres a la clase de caracteres en el patrón de expresión regular. Por ejemplo, el patrón de expresión regular [^ / w. @ - /%] también permite un símbolo de porcentaje y una barra invertida en una cadena de entrada.
Regex.Replace(inputString, @"[!@#$%_]", "");
Referir esto también:
Eliminar caracteres no válidos de la etiqueta de nombre XML - RegEx C #
Aquí hay una función para eliminar los caracteres de una cadena XML especificada:
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace XMLUtils
{
class Standards
{
/// <summary>
/// Strips non-printable ascii characters
/// Refer to http://www.w3.org/TR/xml11/#charsets for XML 1.1
/// Refer to http://www.w3.org/TR/2006/REC-xml-20060816/#charsets for XML 1.0
/// </summary>
/// <param name="content">contents</param>
/// <param name="XMLVersion">XML Specification to use. Can be 1.0 or 1.1</param>
private void StripIllegalXMLChars(string tmpContents, 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(tmpContents))
{
tmpContents = regex.Replace(tmpContents, String.Empty);
}
tmpContents = string.Empty;
}
}
}