Très similaire à cette question , sauf pour Java.
Quelle est la méthode recommandée pour encoder des chaînes pour une sortie XML en Java. Les chaînes peuvent contenir des caractères tels que "&", "<", etc.
Très similaire à cette question , sauf pour Java.
Quelle est la méthode recommandée pour encoder des chaînes pour une sortie XML en Java. Les chaînes peuvent contenir des caractères tels que "&", "<", etc.
Réponses:
Très simplement: utilisez une bibliothèque XML. De cette façon, ce sera en fait correct au lieu d'exiger une connaissance détaillée des bits de la spécification XML.
Comme d'autres l'ont mentionné, l'utilisation d'une bibliothèque XML est le moyen le plus simple. Si vous voulez vous échapper, vous pouvez consulter StringEscapeUtils
la bibliothèque Apache Commons Lang .
StringEscapeUtils.escapeXml(str)
partir de commons-lang
. Je l'utilise dans l'application App Engine - fonctionne comme un charme. Voici le Java Doc pour cette fonction:
\t
, \n
et \r
.
\t
, \n
ou \r
doit être échappé?
Utilisez simplement.
<![CDATA[ your text here ]]>
Cela permettra à tous les caractères sauf la fin
]]>
Vous pouvez donc inclure des caractères qui seraient illégaux tels que & et>. Par exemple.
<element><![CDATA[ characters such as & and > are allowed ]]></element>
Cependant, les attributs devront être échappés car les blocs CDATA ne peuvent pas être utilisés pour eux.
Cela a bien fonctionné pour moi pour fournir une version échappée d'une chaîne de texte:
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();
}
}
Essaye ça:
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
.
Cette question a huit ans et n'est toujours pas une réponse entièrement correcte! Non, vous ne devriez pas avoir à importer une API tierce entière pour effectuer cette tâche simple. Mauvais conseil.
La méthode suivante:
J'ai essayé d'optimiser le cas le plus courant, tout en veillant à ce que vous puissiez diriger / dev / random à travers cela et obtenir une chaîne valide 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();
}
Edit: pour ceux qui continuent d'insister sur le fait qu'il est insensé d'écrire votre propre code pour cela alors qu'il existe de très bonnes API Java pour traiter XML, vous voudrez peut-être savoir que l'API StAX incluse avec Oracle Java 8 (je n'en ai pas testé d'autres ) ne parvient pas à encoder correctement le contenu CDATA: il n'échappe pas aux séquences]]> du contenu. Une bibliothèque tierce, même celle qui fait partie du noyau Java, n'est pas toujours la meilleure option.
StringEscapeUtils.escapeXml()
n'échappe pas aux caractères de contrôle (<0x20). XML 1.1 autorise les caractères de contrôle; XML 1.0 ne le fait pas. Par exemple, XStream.toXML()
sérialisera volontiers les caractères de contrôle d'un objet Java en XML, ce qu'un analyseur XML 1.0 rejettera.
Pour échapper aux caractères de contrôle avec Apache commons-lang, utilisez
NumericEntityEscaper.below(0x20).translate(StringEscapeUtils.escapeXml(str))
public String escapeXml(String s) {
return s.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'");
}
replaceAll
appels est très inefficace, en particulier pour les grandes chaînes. Chaque appel entraîne la création d'un nouvel objet String, qui restera en place jusqu'à la récupération de la mémoire. En outre, chaque appel nécessite de boucler à nouveau la chaîne. Cela pourrait être consolidé en une seule boucle manuelle avec des comparaisons avec chaque caractère cible à chaque itération.
Alors que l'idéalisme dit d'utiliser une bibliothèque XML, à mon humble avis, si vous avez une idée de base de XML, le bon sens et les performances indiquent le modèle jusqu'au bout. C'est sans doute plus lisible aussi. Bien que l'utilisation des routines d'échappement d'une bibliothèque soit probablement une bonne idée.
Considérez ceci: XML était destiné à être écrit par des humains.
Utilisez des bibliothèques pour générer du XML lorsque votre XML comme "objet" modélise mieux votre problème. Par exemple, si des modules enfichables participent au processus de construction de ce XML.
Edit: quant à la façon d'échapper réellement au XML dans les modèles, l'utilisation de CDATA ou escapeXml(string)
de JSTL sont deux bonnes solutions, escapeXml(string)
peuvent être utilisées comme ceci:
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<item>${fn:escapeXml(value)}</item>
Le comportement de StringEscapeUtils.escapeXml () est passé de Commons Lang 2.5 à 3.0. Il n'échappe plus aux caractères Unicode supérieurs à 0x7f.
C'est une bonne chose, l'ancienne méthode était d'être un peu trop désireux d'échapper aux entités qui pouvaient simplement être insérées dans un document utf8.
Les nouveaux escapers à inclure dans Google Guava 11.0 semblent également prometteurs: http://code.google.com/p/guava-libraries/issues/detail?id=799
Pour ceux qui recherchent la solution la plus rapide à écrire: utilisez les méthodes d' apache commons-lang :
StringEscapeUtils.escapeXml10()
pour xml 1.0StringEscapeUtils.escapeXml11()
pour xml 1.1StringEscapeUtils.escapeXml()
est désormais obsolète, mais était couramment utilisé dans le passéN'oubliez pas d'inclure la dépendance:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version> <!--check current version! -->
</dependency>
Remarque: votre question concerne l' échappement , pas l' encodage . L'échappement consiste à utiliser <, etc. pour permettre à l'analyseur de distinguer «ceci est une commande XML» et «ceci est du texte». L'encodage est ce que vous spécifiez dans l'en-tête XML (UTF-8, ISO-8859-1, etc.).
Tout d'abord, comme tout le monde l'a dit, utilisez une bibliothèque XML. XML semble simple mais l'encodage + les éléments d'échappement sont du vaudou sombre (que vous remarquerez dès que vous rencontrerez des trémas et du japonais et d'autres trucs bizarres comme des " chiffres pleine largeur " (& # FF11; vaut 1)). Garder XML lisible par l'homme est une tâche de Sisyphe.
Je suggère de ne jamais essayer d'être intelligent en matière d'encodage de texte et d'échappatoire en XML. Mais ne laissez pas cela vous empêcher d'essayer; rappelez-vous simplement quand il vous mord (et il le fera).
Cela dit, si vous n'utilisez que UTF-8, pour rendre les choses plus lisibles, vous pouvez envisager cette stratégie:
<![CDATA[ ... ]]>
J'utilise ceci dans un éditeur SQL et cela permet aux développeurs de couper et coller du SQL à partir d'un outil SQL tiers dans le XML sans se soucier de s'échapper. Cela fonctionne parce que le SQL ne peut pas contenir de trémas dans notre cas, donc je suis en sécurité.
Bien que je sois d'accord avec Jon Skeet en principe, je n'ai parfois pas la possibilité d'utiliser une bibliothèque XML externe. Et je trouve étrange que les deux fonctions pour échapper / échapper à une valeur simple (attribut ou balise, pas de document complet) ne sont pas disponibles dans les bibliothèques XML standard incluses avec Java.
En conséquence et sur la base des différentes réponses que j'ai vues postées ici et ailleurs, voici la solution que j'ai fini par créer (rien ne fonctionnait comme un simple copier / coller):
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;
}
Ce qui précède s'adapte à plusieurs choses différentes:
À un moment donné, j'écrirai l'inversion de cette fonction, toUnescaped (). Je n'ai tout simplement pas le temps de faire ça aujourd'hui. Quand je le ferai, je viendrai mettre à jour cette réponse avec le code. :)
null
caractère. Pouvez-vous expliquer la définition des deux valeurs, UNICODE_LOW
et UNICODE_HIGH
? Veuillez relire le if
qui utilise ces deux valeurs. Remarquez null
( \u0000
ce qui est (int)0
) ne tombe pas entre ces deux valeurs. Lisez comment il devient correctement «échappé», tout comme TOUS les caractères Unicode existant en dehors de la plage UNICODE_LOW
et UNICODE_HIGH
, en utilisant la &#
technique.
Pour échapper aux caractères XML, le moyen le plus simple est d'utiliser le projet Apache Commons Lang, JAR téléchargeable sur: http://commons.apache.org/lang/
La classe est la suivante: org.apache.commons.lang3.StringEscapeUtils;
Il a une méthode nommée "escapeXml", qui renverra une chaîne d'échappement appropriée.
Si vous recherchez une bibliothèque pour faire le travail, essayez:
Guava 26.0 documenté ici
return XmlEscapers.xmlContentEscaper().escape(text);
Remarque: il existe également un
xmlAttributeEscaper()
Apache Commons Text 1.4 documenté ici
StringEscapeUtils.escapeXml11(text)
Remarque: il existe également une
escapeXml10()
méthode
Voici une solution simple et idéale pour encoder des caractères accentués!
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);
Les sorties
Hi Lârry & Môe!
Vous pouvez utiliser la bibliothèque ESAPI (Enterprise Security API) , qui fournit des méthodes telles que encodeForXML
et encodeForXMLAttribute
. Jetez un œil à la documentation de l' interface Encoder ; il contient également des exemples de création d'une instance de DefaultEncoder .
Remplacez simplement
& with &
Et pour les autres personnages:
> with >
< with <
\" with "
' with '
Utilisez JAXP et oubliez la gestion du texte, cela se fera automatiquement pour vous.
Essayez d'encoder le XML à l'aide du sérialiseur XML Apache
//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());
Voici ce que j'ai trouvé après avoir cherché partout une solution:
Obtenez la bibliothèque Jsoup:
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.12.1</version>
</dependency>
Ensuite:
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()
J'espère que cela aide quelqu'un