Molto simile a questa domanda , ad eccezione di Java.
Qual è il modo consigliato per codificare le stringhe per un output XML in Java. Le stringhe potrebbero contenere caratteri come "&", "<" e così via.
Molto simile a questa domanda , ad eccezione di Java.
Qual è il modo consigliato per codificare le stringhe per un output XML in Java. Le stringhe potrebbero contenere caratteri come "&", "<" e così via.
Risposte:
Molto semplicemente: usa una libreria XML. In questo modo sarà effettivamente giusto invece di richiedere una conoscenza dettagliata dei bit delle specifiche XML.
Come altri hanno già detto, l'utilizzo di una libreria XML è il modo più semplice. Se vuoi scappare da te stesso, puoi cercare StringEscapeUtils
dalla libreria Apache Commons Lang .
StringEscapeUtils.escapeXml(str)
da commons-lang
. Lo uso nell'applicazione App Engine: funziona a meraviglia. Ecco il Java Doc per questa funzione:
\t
, \n
e \r
.
\t
, \n
o \r
deve essere evitato?
Basta usare.
<![CDATA[ your text here ]]>
Ciò consentirà qualsiasi carattere tranne il finale
]]>
Quindi puoi includere caratteri illegali come & e>. Per esempio.
<element><![CDATA[ characters such as & and > are allowed ]]></element>
Tuttavia, sarà necessario eseguire l'escape degli attributi poiché i blocchi CDATA non possono essere utilizzati per essi.
Questo ha funzionato bene per me per fornire una versione con escape di una stringa di testo:
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();
}
}
Prova questo:
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
.
Questa domanda ha otto anni e non è ancora una risposta del tutto corretta! No, non dovresti importare un'intera API di terze parti per eseguire questa semplice operazione. Cattivo consiglio.
Il metodo seguente:
Ho cercato di ottimizzare per il caso più comune, assicurandomi comunque di poter eseguire il pipe / dev / random attraverso questo e ottenere una stringa valida in 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();
}
Modifica: per coloro che continuano a insistere che è sciocco scrivere il proprio codice per questo quando ci sono API Java perfettamente buone per gestire XML, potresti voler sapere che l'API StAX inclusa con Oracle Java 8 (non ne ho testate altre ) non riesce a codificare correttamente il contenuto CDATA: non sfugge]]> sequenze nel contenuto. Una libreria di terze parti, anche una che fa parte del core Java, non è sempre l'opzione migliore.
StringEscapeUtils.escapeXml()
non sfugge ai caratteri di controllo (<0x20). XML 1.1 consente caratteri di controllo; XML 1.0 non lo fa. Ad esempio, XStream.toXML()
serializzerà felicemente i caratteri di controllo di un oggetto Java in XML, che un parser XML 1.0 rifiuterà.
Per sfuggire ai personaggi di controllo con Apache commons-lang, usa
NumericEntityEscaper.below(0x20).translate(StringEscapeUtils.escapeXml(str))
public String escapeXml(String s) {
return s.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'");
}
replaceAll
chiamate è molto inefficiente, soprattutto per le stringhe di grandi dimensioni. Ogni chiamata risulta nella creazione di un nuovo oggetto String, che rimarrà in sospeso fino alla raccolta dei rifiuti. Inoltre, ogni chiamata richiede di ripetere il ciclo attraverso la stringa. Questo potrebbe essere consolidato in un unico ciclo manuale con confronti con ogni carattere di destinazione in ogni iterazione.
Mentre l'idealismo dice di usare una libreria XML, IMHO se hai un'idea di base di XML, il buon senso e le prestazioni dicono che il modello è tutto. È probabilmente anche più leggibile. Anche se l'uso delle routine di escape di una libreria è probabilmente una buona idea.
Considerate questo: XML è stato pensato per essere scritto da esseri umani.
Usa le librerie per generare XML quando il tuo XML come "oggetto" modella meglio il tuo problema. Ad esempio, se i moduli collegabili partecipano al processo di creazione di questo XML.
Modifica: per quanto riguarda come sfuggire effettivamente a XML nei modelli, l'uso di CDATA o escapeXml(string)
da JSTL sono due buone soluzioni, escapeXml(string)
possono essere utilizzate in questo modo:
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<item>${fn:escapeXml(value)}</item>
Il comportamento di StringEscapeUtils.escapeXml () è stato modificato da Commons Lang 2.5 a 3.0. Ora non esegue più l'escape dei caratteri Unicode maggiori di 0x7f.
Questa è una buona cosa, il vecchio metodo doveva essere un po 'impaziente di sfuggire a entità che potevano essere semplicemente inserite in un documento utf8.
Anche i nuovi escapers da includere in Google Guava 11.0 sembrano promettenti: http://code.google.com/p/guava-libraries/issues/detail?id=799
Per coloro che cercano la soluzione più veloce da scrivere: utilizzare i metodi di apache commons-lang :
StringEscapeUtils.escapeXml10()
per xml 1.0StringEscapeUtils.escapeXml11()
per xml 1.1StringEscapeUtils.escapeXml()
è ora deprecato, ma è stato utilizzato comunemente in passatoRicorda di includere la dipendenza:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version> <!--check current version! -->
</dependency>
Nota: la tua domanda riguarda l' escape , non la codifica . L'escape sta usando <, ecc. Per consentire al parser di distinguere tra "questo è un comando XML" e "questo è un testo". La codifica è ciò che specifichi nell'intestazione XML (UTF-8, ISO-8859-1, ecc.).
Prima di tutto, come hanno detto tutti gli altri, usa una libreria XML. XML sembra semplice ma la codifica + le cose di escape sono dark voodoo (che noterai non appena incontrerai dieresi e giapponese e altre cose strane come " cifre a larghezza intera " (& # FF11; è 1)). Mantenere XML leggibile dagli umani è un compito di Sisyphus.
Suggerisco di non provare mai ad essere intelligente sulla codifica del testo e sull'escape in XML. Ma non lasciare che questo ti impedisca di provare; ricorda solo quando ti morde (e lo farà).
Detto questo, se usi solo UTF-8, per rendere le cose più leggibili puoi considerare questa strategia:
<![CDATA[ ... ]]>
Lo sto usando in un editor SQL e consente agli sviluppatori di tagliare e incollare SQL da uno strumento SQL di terze parti nell'XML senza doversi preoccupare dell'escape. Questo funziona perché l'SQL non può contenere dieresi nel nostro caso, quindi sono al sicuro.
Anche se in linea di principio sono d'accordo con Jon Skeet, a volte non ho la possibilità di utilizzare una libreria XML esterna. E trovo peculiare che le due funzioni per eseguire l'escape / unescape di un valore semplice (attributo o tag, documento non completo) non siano disponibili nelle librerie XML standard incluse in Java.
Di conseguenza e in base alle diverse risposte che ho visto pubblicate qui e altrove, ecco la soluzione che ho finito per creare (niente ha funzionato come un semplice copia / incolla):
public final static String ESCAPE_CHARS = "<>&\"\'";
public final static List<String> ESCAPE_STRINGS = Collections.unmodifiableList(Arrays.asList(new String[] {
"<"
, ">"
, "&"
, """
, "'"
}));
private static String UNICODE_LOW = "" + ((char)0x20); //space
private static String UNICODE_HIGH = "" + ((char)0x7f);
//should only use 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 {
stringBuilder.append("&#" + ((int)character.charAt(0)) + ";");
modified = true;
}
}
}
if (modified) {
result = stringBuilder.toString();
}
}
return result;
}
Quanto sopra ospita diverse cose:
Ad un certo punto scriverò l'inversione di questa funzione, toUnescaped (). Non ho tempo per farlo oggi. Quando lo farò, verrò ad aggiornare questa risposta con il codice. :)
Per sfuggire ai caratteri XML, il modo più semplice è utilizzare il progetto Apache Commons Lang, JAR scaricabile da: http://commons.apache.org/lang/
La classe è questa: org.apache.commons.lang3.StringEscapeUtils;
Ha un metodo chiamato "escapeXml", che restituirà una stringa con escape appropriato.
Se stai cercando una libreria per portare a termine il lavoro, prova:
Guava 26.0 documentato qui
return XmlEscapers.xmlContentEscaper().escape(text);
Nota: esiste anche un file
xmlAttributeEscaper()
Apache Commons Text 1.4 documentato qui
StringEscapeUtils.escapeXml11(text)
Nota: esiste anche un
escapeXml10()
metodo
Ecco una soluzione semplice ed è ottima anche per codificare i caratteri accentati!
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);
Uscite
Hi Lârry & Môe!
È possibile utilizzare la libreria ESAPI (Enterprise Security API) , che fornisce metodi come encodeForXML
e encodeForXMLAttribute
. Dai un'occhiata alla documentazione dell'interfaccia Encoder ; contiene anche esempi di come creare un'istanza di DefaultEncoder .
Basta sostituire
& with &
E per altri personaggi:
> with >
< with <
\" with "
' with '
Usa JAXP e dimentica la gestione del testo, sarà fatto automaticamente.
Prova a codificare l'XML usando il serializzatore XML di 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());
Ecco cosa ho trovato dopo aver cercato ovunque una soluzione:
Ottieni la libreria Jsoup:
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.12.1</version>
</dependency>
Poi:
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()
Spero che questo aiuti qualcuno