Muy similar a esta pregunta , excepto para Java.
¿Cuál es la forma recomendada de codificar cadenas para una salida XML en Java? Las cadenas pueden contener caracteres como "&", "<", etc.
Muy similar a esta pregunta , excepto para Java.
¿Cuál es la forma recomendada de codificar cadenas para una salida XML en Java? Las cadenas pueden contener caracteres como "&", "<", etc.
Respuestas:
Muy simple: use una biblioteca XML. De esa manera, será realmente correcto en lugar de requerir un conocimiento detallado de los bits de la especificación XML.
Como han mencionado otros, usar una biblioteca XML es la forma más sencilla. Si desea escapar, puede buscar en StringEscapeUtils
la biblioteca Apache Commons Lang .
StringEscapeUtils.escapeXml(str)
desde commons-lang
. Lo uso en la aplicación App Engine: funciona como un encanto. Aquí está el documento de Java para esta función:
\t
, \n
y \r
.
\t
, ¿ \n
o \r
es necesario escapar?
Solo usa.
<![CDATA[ your text here ]]>
Esto permitirá que cualquier personaje excepto el final
]]>
Por lo tanto, puede incluir caracteres que serían ilegales como & y>. Por ejemplo.
<element><![CDATA[ characters such as & and > are allowed ]]></element>
Sin embargo, los atributos deberán escaparse ya que los bloques CDATA no se pueden usar para ellos.
Esto me ha funcionado bien para proporcionar una versión de escape de una cadena de texto:
public class XMLHelper {
/**
* Returns the string where all non-ascii and <, &, > are encoded as numeric entities. I.e. "<A & B >"
* .... (insert result here). The result is safe to include anywhere in a text field in an XML-string. If there was
* no characters to protect, the original string is returned.
*
* @param originalUnprotectedString
* original string which may contain characters either reserved in XML or with different representation
* in different encodings (like 8859-1 and UFT-8)
* @return
*/
public static String protectSpecialCharacters(String originalUnprotectedString) {
if (originalUnprotectedString == null) {
return null;
}
boolean anyCharactersProtected = false;
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < originalUnprotectedString.length(); i++) {
char ch = originalUnprotectedString.charAt(i);
boolean controlCharacter = ch < 32;
boolean unicodeButNotAscii = ch > 126;
boolean characterWithSpecialMeaningInXML = ch == '<' || ch == '&' || ch == '>';
if (characterWithSpecialMeaningInXML || unicodeButNotAscii || controlCharacter) {
stringBuffer.append("&#" + (int) ch + ";");
anyCharactersProtected = true;
} else {
stringBuffer.append(ch);
}
}
if (anyCharactersProtected == false) {
return originalUnprotectedString;
}
return stringBuffer.toString();
}
}
Prueba esto:
String xmlEscapeText(String t) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < t.length(); i++){
char c = t.charAt(i);
switch(c){
case '<': sb.append("<"); break;
case '>': sb.append(">"); break;
case '\"': sb.append("""); break;
case '&': sb.append("&"); break;
case '\'': sb.append("'"); break;
default:
if(c>0x7e) {
sb.append("&#"+((int)c)+";");
}else
sb.append(c);
}
}
return sb.toString();
}
t==null
.
¡Esta pregunta tiene ocho años y aún no es una respuesta completamente correcta! No, no debería tener que importar una API de terceros completa para realizar esta sencilla tarea. Mal consejo.
El siguiente método:
Intenté optimizar para el caso más común, sin dejar de asegurarme de que podría canalizar / dev / random a través de esto y obtener una cadena válida en XML.
public static String encodeXML(CharSequence s) {
StringBuilder sb = new StringBuilder();
int len = s.length();
for (int i=0;i<len;i++) {
int c = s.charAt(i);
if (c >= 0xd800 && c <= 0xdbff && i + 1 < len) {
c = ((c-0xd7c0)<<10) | (s.charAt(++i)&0x3ff); // UTF16 decode
}
if (c < 0x80) { // ASCII range: test most common case first
if (c < 0x20 && (c != '\t' && c != '\r' && c != '\n')) {
// Illegal XML character, even encoded. Skip or substitute
sb.append("�"); // Unicode replacement character
} else {
switch(c) {
case '&': sb.append("&"); break;
case '>': sb.append(">"); break;
case '<': sb.append("<"); break;
// Uncomment next two if encoding for an XML attribute
// case '\'' sb.append("'"); break;
// case '\"' sb.append("""); break;
// Uncomment next three if you prefer, but not required
// case '\n' sb.append(" "); break;
// case '\r' sb.append(" "); break;
// case '\t' sb.append("	"); break;
default: sb.append((char)c);
}
}
} else if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff) {
// Illegal XML character, even encoded. Skip or substitute
sb.append("�"); // Unicode replacement character
} else {
sb.append("&#x");
sb.append(Integer.toHexString(c));
sb.append(';');
}
}
return sb.toString();
}
Editar: para aquellos que continúan insistiendo en que es una tontería escribir su propio código para esto cuando hay API de Java perfectamente buenas para tratar con XML, es posible que desee saber que la API de StAX incluida con Oracle Java 8 (no he probado otras ) no codifica correctamente el contenido CDATA: no escapa]]> secuencias en el contenido. Una biblioteca de terceros, incluso una que sea parte del núcleo de Java, no siempre es la mejor opción.
StringEscapeUtils.escapeXml()
no escapa a los caracteres de control (<0x20). XML 1.1 permite caracteres de control; XML 1.0 no lo hace. Por ejemplo,XStream.toXML()
felizmente serializará los caracteres de control de un objeto Java en XML, que un analizador XML 1.0 rechazará.
Para escapar de los caracteres de control con Apache commons-lang, use
NumericEntityEscaper.below(0x20).translate(StringEscapeUtils.escapeXml(str))
public String escapeXml(String s) {
return s.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'");
}
replaceAll
llamadas es muy ineficaz, especialmente para cadenas grandes. Cada llamada da como resultado la creación de un nuevo objeto String, que permanecerá hasta que se recoja la basura. Además, cada llamada requiere volver a recorrer la cadena. Esto podría consolidarse en un solo bucle manual con comparaciones con cada carácter objetivo en cada iteración.
Si bien el idealismo dice que use una biblioteca XML, en mi humilde opinión, si tiene una idea básica de XML, el sentido común y el rendimiento lo dicen todo el camino. Podría decirse que también es más legible. Aunque usar las rutinas de escape de una biblioteca probablemente sea una buena idea.
Considere esto: XML estaba destinado a ser escrito por humanos.
Utilice bibliotecas para generar XML cuando tenga su XML como un "objeto" que modele mejor su problema. Por ejemplo, si los módulos conectables participan en el proceso de construcción de este XML.
Editar: en cuanto a cómo escapar de XML en plantillas, el uso de CDATA o escapeXml(string)
de JSTL son dos buenas soluciones, escapeXml(string)
se pueden usar así:
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<item>${fn:escapeXml(value)}</item>
El comportamiento de StringEscapeUtils.escapeXml () ha cambiado de Commons Lang 2.5 a 3.0. Ahora ya no escapa a los caracteres Unicode superiores a 0x7f.
Esto es algo bueno, el método antiguo era estar un poco ansioso por escapar de las entidades que podrían simplemente insertarse en un documento utf8.
Los nuevos escapers que se incluirán en Google Guava 11.0 también parecen prometedores: http://code.google.com/p/guava-libraries/issues/detail?id=799
Para aquellos que buscan la solución más rápida de escribir: use métodos de apache commons-lang :
StringEscapeUtils.escapeXml10()
para xml 1.0StringEscapeUtils.escapeXml11()
para xml 1.1StringEscapeUtils.escapeXml()
ahora está en desuso, pero se usaba comúnmente en el pasadoRecuerde incluir la dependencia:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version> <!--check current version! -->
</dependency>
Nota: Su pregunta es sobre escapar , no codificar . Escapar es usar <, etc. para permitir que el analizador distinga entre "esto es un comando XML" y "esto es un texto". La codificación es lo que especifica en el encabezado XML (UTF-8, ISO-8859-1, etc.).
En primer lugar, como todos los demás dijeron, use una biblioteca XML. XML parece simple pero la codificación + el material de escape es un vudú oscuro (que notarás tan pronto como encuentres diéresis y japonés y otras cosas raras como " dígitos de ancho completo " (& # FF11; es 1)). Mantener XML legible por humanos es una tarea de Sisyphus.
Sugiero que nunca intentes ser inteligente sobre la codificación de texto y el escape en XML. Pero no dejes que eso te impida intentarlo; solo recuerda cuando te muerda (y lo hará).
Dicho esto, si usa solo UTF-8, para hacer las cosas más legibles, puede considerar esta estrategia:
<![CDATA[ ... ]]>
Estoy usando esto en un editor de SQL y permite a los desarrolladores cortar y pegar SQL desde una herramienta SQL de terceros en el XML sin preocuparse por escapar. Esto funciona porque el SQL no puede contener diéresis en nuestro caso, así que estoy a salvo.
Si bien estoy de acuerdo con Jon Skeet en principio, a veces no tengo la opción de usar una biblioteca XML externa. Y me parece peculiar que las dos funciones para escapar / no escapar de un valor simple (atributo o etiqueta, no documento completo) no están disponibles en las bibliotecas XML estándar incluidas con Java.
Como resultado y en base a las diferentes respuestas que he visto publicadas aquí y en otros lugares, aquí está la solución que terminé creando (nada funcionó como una simple copia / pegado):
public final static String ESCAPE_CHARS = "<>&\"\'";
public final static List<String> ESCAPE_STRINGS = Collections.unmodifiableList(Arrays.asList(new String[] {
"<"
, ">"
, "&"
, """
, "'"
}));
private static String UNICODE_NULL = "" + ((char)0x00); //null
private static String UNICODE_LOW = "" + ((char)0x20); //space
private static String UNICODE_HIGH = "" + ((char)0x7f);
//should only be used for the content of an attribute or tag
public static String toEscaped(String content) {
String result = content;
if ((content != null) && (content.length() > 0)) {
boolean modified = false;
StringBuilder stringBuilder = new StringBuilder(content.length());
for (int i = 0, count = content.length(); i < count; ++i) {
String character = content.substring(i, i + 1);
int pos = ESCAPE_CHARS.indexOf(character);
if (pos > -1) {
stringBuilder.append(ESCAPE_STRINGS.get(pos));
modified = true;
}
else {
if ( (character.compareTo(UNICODE_LOW) > -1)
&& (character.compareTo(UNICODE_HIGH) < 1)
) {
stringBuilder.append(character);
}
else {
//Per URL reference below, Unicode null character is always restricted from XML
//URL: https://en.wikipedia.org/wiki/Valid_characters_in_XML
if (character.compareTo(UNICODE_NULL) != 0) {
stringBuilder.append("&#" + ((int)character.charAt(0)) + ";");
}
modified = true;
}
}
}
if (modified) {
result = stringBuilder.toString();
}
}
return result;
}
Lo anterior se adapta a varias cosas diferentes:
En algún momento, escribiré la inversión de esta función, toUnescaped (). Simplemente no tengo tiempo para hacer eso hoy. Cuando lo haga, actualizaré esta respuesta con el código. :)
null
carácter. ¿Puede explicar la definición de los dos valores UNICODE_LOW
y UNICODE_HIGH
? Vuelva a leer el if
que usa esos dos valores. Observe null
( \u0000
cuál es (int)0
) no cae entre estos dos valores. Lea cómo se "escapa" correctamente al igual que TODOS los caracteres Unicode que existen fuera del rango UNICODE_LOW
y UNICODE_HIGH
, utilizando la &#
técnica.
Para escapar de los caracteres XML, la forma más sencilla es utilizar el proyecto Apache Commons Lang, JAR descargable desde: http://commons.apache.org/lang/
La clase es esta: org.apache.commons.lang3.StringEscapeUtils;
Tiene un método llamado "escapeXml", que devolverá un String con el escape apropiado.
Si está buscando una biblioteca para hacer el trabajo, intente:
Guava 26.0 documentado aquí
return XmlEscapers.xmlContentEscaper().escape(text);
Nota: también hay una
xmlAttributeEscaper()
Apache Commons Text 1.4 documentado aquí
StringEscapeUtils.escapeXml11(text)
Nota: también hay un
escapeXml10()
método
Esta es una solución fácil y también es ideal para codificar caracteres acentuados.
String in = "Hi Lârry & Môe!";
StringBuilder out = new StringBuilder();
for(int i = 0; i < in.length(); i++) {
char c = in.charAt(i);
if(c < 31 || c > 126 || "<>\"'\\&".indexOf(c) >= 0) {
out.append("&#" + (int) c + ";");
} else {
out.append(c);
}
}
System.out.printf("%s%n", out);
Salidas
Hi Lârry & Môe!
Puede utilizar la biblioteca Enterprise Security API (ESAPI) , que proporciona métodos como encodeForXML
y encodeForXMLAttribute
. Eche un vistazo a la documentación de la interfaz del codificador ; también contiene ejemplos de cómo crear una instancia de DefaultEncoder .
Solo reemplaza
& with &
Y para otros personajes:
> with >
< with <
\" with "
' with '
Use JAXP y olvídese del manejo de texto, se hará automáticamente.
Intente codificar el XML usando el serializador Apache XML
//Serialize DOM
OutputFormat format = new OutputFormat (doc);
// as a String
StringWriter stringOut = new StringWriter ();
XMLSerializer serial = new XMLSerializer (stringOut,
format);
serial.serialize(doc);
// Display the XML
System.out.println(stringOut.toString());
Esto es lo que encontré después de buscar en todas partes buscando una solución:
Obtenga la biblioteca Jsoup:
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.12.1</version>
</dependency>
Luego:
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Entities
import org.jsoup.parser.Parser
String xml = '''<?xml version = "1.0"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
SOAP-ENV:encodingStyle = "http://www.w3.org/2001/12/soap-encoding">
<SOAP-ENV:Body xmlns:m = "http://www.example.org/quotations">
<m:GetQuotation>
<m:QuotationsName> MiscroSoft@G>>gle.com </m:QuotationsName>
</m:GetQuotation>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'''
Document doc = Jsoup.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")), "UTF-8", "", Parser.xmlParser())
doc.outputSettings().charset("UTF-8")
doc.outputSettings().escapeMode(Entities.EscapeMode.base)
println doc.toString()
Espero que esto ayude a alguien