Come annullare l'escape delle entità di caratteri HTML in Java?


147

Fondamentalmente vorrei decodificare un determinato documento Html e sostituire tutti i caratteri speciali, come " "-> " ", ">"-> ">".

In .NET possiamo farne uso HttpUtility.HtmlDecode.

Qual è la funzione equivalente in Java?


4
& nbsp; si chiama entità personaggio. Modificato il titolo.
Eugene Yokota,

Risposte:


182

Per questo ho usato Apache Commons StringEscapeUtils.unescapeHtml4 () :

Annulla l'escaping di una stringa contenente entità sfugge a una stringa contenente i caratteri Unicode effettivi corrispondenti alle escape. Supporta entità HTML 4.0.


19
Purtroppo mi sono appena reso conto oggi che non decodifica molto bene i caratteri speciali HTML :(
Sid

1
un trucco sporco è quello di memorizzare il valore inizialmente in un campo nascosto per evitarlo, quindi il campo target dovrebbe ottenere il valore dal campo nascosto.
setzamora,

2
La classe StringEscapeUtils è obsoleta e spostata nel testo comune di Apache
Pauli

2
Voglio convertire la stringa <p>&uuml;&egrave;</p>in <p>üé</p>, con StringEscapeUtils.unescapeHtml4()ottengo &lt;p&gt;üè&lt;/p&gt;. C'è un modo per mantenere intatti i tag html esistenti?
Nickkk,

48

Le biblioteche citate in altre risposte sarebbero ottime soluzioni, ma se ti capita già di scavare nel mondo reale html nel tuo progetto, il Jsoupprogetto ha molto di più da offrire oltre alla semplice gestione di cose da "e commerciale e punto e virgola FFFF" .

// textValue: <p>This is a&nbsp;sample. \"Granny\" Smith &#8211;.<\/p>\r\n
// becomes this: This is a sample. "Granny" Smith –.
// with one line of code:
// Jsoup.parse(textValue).getText(); // for older versions of Jsoup
Jsoup.parse(textValue).text();

// Another possibility may be the static unescapeEntities method:
boolean strictMode = true;
String unescapedString = org.jsoup.parser.Parser.unescapeEntities(textValue, strictMode);

E ottieni anche la comoda API per l'estrazione e la manipolazione dei dati, utilizzando i migliori metodi DOM, CSS e simili a jquery. È licenza open source e MIT.


3
upvote +, ma dovrei indicare che le versioni più recenti di Jsoup usano .text()invece di.getText()
SourceVisor

4
Forse è più diretto da usare org.jsoup.parser.Parser.unescapeEntities(String string, boolean inAttribute). Documenti API: jsoup.org/apidocs/org/jsoup/parser/…
danneu

3
Questo è stato perfetto, poiché sto già usando Jsoup nel mio progetto. Inoltre, @danneu aveva ragione: Parser.unescapeEntities funziona esattamente come pubblicizzato.
MandisaW

42

Ho provato Apache Commons StringEscapeUtils.unescapeHtml3 () nel mio progetto, ma non ero soddisfatto delle sue prestazioni. Si scopre che fa molte operazioni non necessarie. Per uno, alloca uno StringWriter per ogni chiamata, anche se non c'è nulla da annullare lo scappamento nella stringa. Ho riscritto quel codice in modo diverso, ora funziona molto più velocemente. Chiunque lo trovi su Google, può usarlo.

Il codice seguente rimuove tutti i simboli HTML 3 e le escape numeriche (equivalenti a Apache unescapeHtml3). Puoi semplicemente aggiungere più voci alla mappa se hai bisogno di HTML 4.

package com.example;

import java.io.StringWriter;
import java.util.HashMap;

public class StringUtils {

    public static final String unescapeHtml3(final String input) {
        StringWriter writer = null;
        int len = input.length();
        int i = 1;
        int st = 0;
        while (true) {
            // look for '&'
            while (i < len && input.charAt(i-1) != '&')
                i++;
            if (i >= len)
                break;

            // found '&', look for ';'
            int j = i;
            while (j < len && j < i + MAX_ESCAPE + 1 && input.charAt(j) != ';')
                j++;
            if (j == len || j < i + MIN_ESCAPE || j == i + MAX_ESCAPE + 1) {
                i++;
                continue;
            }

            // found escape 
            if (input.charAt(i) == '#') {
                // numeric escape
                int k = i + 1;
                int radix = 10;

                final char firstChar = input.charAt(k);
                if (firstChar == 'x' || firstChar == 'X') {
                    k++;
                    radix = 16;
                }

                try {
                    int entityValue = Integer.parseInt(input.substring(k, j), radix);

                    if (writer == null) 
                        writer = new StringWriter(input.length());
                    writer.append(input.substring(st, i - 1));

                    if (entityValue > 0xFFFF) {
                        final char[] chrs = Character.toChars(entityValue);
                        writer.write(chrs[0]);
                        writer.write(chrs[1]);
                    } else {
                        writer.write(entityValue);
                    }

                } catch (NumberFormatException ex) { 
                    i++;
                    continue;
                }
            }
            else {
                // named escape
                CharSequence value = lookupMap.get(input.substring(i, j));
                if (value == null) {
                    i++;
                    continue;
                }

                if (writer == null) 
                    writer = new StringWriter(input.length());
                writer.append(input.substring(st, i - 1));

                writer.append(value);
            }

            // skip escape
            st = j + 1;
            i = st;
        }

        if (writer != null) {
            writer.append(input.substring(st, len));
            return writer.toString();
        }
        return input;
    }

    private static final String[][] ESCAPES = {
        {"\"",     "quot"}, // " - double-quote
        {"&",      "amp"}, // & - ampersand
        {"<",      "lt"}, // < - less-than
        {">",      "gt"}, // > - greater-than

        // Mapping to escape ISO-8859-1 characters to their named HTML 3.x equivalents.
        {"\u00A0", "nbsp"}, // non-breaking space
        {"\u00A1", "iexcl"}, // inverted exclamation mark
        {"\u00A2", "cent"}, // cent sign
        {"\u00A3", "pound"}, // pound sign
        {"\u00A4", "curren"}, // currency sign
        {"\u00A5", "yen"}, // yen sign = yuan sign
        {"\u00A6", "brvbar"}, // broken bar = broken vertical bar
        {"\u00A7", "sect"}, // section sign
        {"\u00A8", "uml"}, // diaeresis = spacing diaeresis
        {"\u00A9", "copy"}, // © - copyright sign
        {"\u00AA", "ordf"}, // feminine ordinal indicator
        {"\u00AB", "laquo"}, // left-pointing double angle quotation mark = left pointing guillemet
        {"\u00AC", "not"}, // not sign
        {"\u00AD", "shy"}, // soft hyphen = discretionary hyphen
        {"\u00AE", "reg"}, // ® - registered trademark sign
        {"\u00AF", "macr"}, // macron = spacing macron = overline = APL overbar
        {"\u00B0", "deg"}, // degree sign
        {"\u00B1", "plusmn"}, // plus-minus sign = plus-or-minus sign
        {"\u00B2", "sup2"}, // superscript two = superscript digit two = squared
        {"\u00B3", "sup3"}, // superscript three = superscript digit three = cubed
        {"\u00B4", "acute"}, // acute accent = spacing acute
        {"\u00B5", "micro"}, // micro sign
        {"\u00B6", "para"}, // pilcrow sign = paragraph sign
        {"\u00B7", "middot"}, // middle dot = Georgian comma = Greek middle dot
        {"\u00B8", "cedil"}, // cedilla = spacing cedilla
        {"\u00B9", "sup1"}, // superscript one = superscript digit one
        {"\u00BA", "ordm"}, // masculine ordinal indicator
        {"\u00BB", "raquo"}, // right-pointing double angle quotation mark = right pointing guillemet
        {"\u00BC", "frac14"}, // vulgar fraction one quarter = fraction one quarter
        {"\u00BD", "frac12"}, // vulgar fraction one half = fraction one half
        {"\u00BE", "frac34"}, // vulgar fraction three quarters = fraction three quarters
        {"\u00BF", "iquest"}, // inverted question mark = turned question mark
        {"\u00C0", "Agrave"}, // А - uppercase A, grave accent
        {"\u00C1", "Aacute"}, // Б - uppercase A, acute accent
        {"\u00C2", "Acirc"}, // В - uppercase A, circumflex accent
        {"\u00C3", "Atilde"}, // Г - uppercase A, tilde
        {"\u00C4", "Auml"}, // Д - uppercase A, umlaut
        {"\u00C5", "Aring"}, // Е - uppercase A, ring
        {"\u00C6", "AElig"}, // Ж - uppercase AE
        {"\u00C7", "Ccedil"}, // З - uppercase C, cedilla
        {"\u00C8", "Egrave"}, // И - uppercase E, grave accent
        {"\u00C9", "Eacute"}, // Й - uppercase E, acute accent
        {"\u00CA", "Ecirc"}, // К - uppercase E, circumflex accent
        {"\u00CB", "Euml"}, // Л - uppercase E, umlaut
        {"\u00CC", "Igrave"}, // М - uppercase I, grave accent
        {"\u00CD", "Iacute"}, // Н - uppercase I, acute accent
        {"\u00CE", "Icirc"}, // О - uppercase I, circumflex accent
        {"\u00CF", "Iuml"}, // П - uppercase I, umlaut
        {"\u00D0", "ETH"}, // Р - uppercase Eth, Icelandic
        {"\u00D1", "Ntilde"}, // С - uppercase N, tilde
        {"\u00D2", "Ograve"}, // Т - uppercase O, grave accent
        {"\u00D3", "Oacute"}, // У - uppercase O, acute accent
        {"\u00D4", "Ocirc"}, // Ф - uppercase O, circumflex accent
        {"\u00D5", "Otilde"}, // Х - uppercase O, tilde
        {"\u00D6", "Ouml"}, // Ц - uppercase O, umlaut
        {"\u00D7", "times"}, // multiplication sign
        {"\u00D8", "Oslash"}, // Ш - uppercase O, slash
        {"\u00D9", "Ugrave"}, // Щ - uppercase U, grave accent
        {"\u00DA", "Uacute"}, // Ъ - uppercase U, acute accent
        {"\u00DB", "Ucirc"}, // Ы - uppercase U, circumflex accent
        {"\u00DC", "Uuml"}, // Ь - uppercase U, umlaut
        {"\u00DD", "Yacute"}, // Э - uppercase Y, acute accent
        {"\u00DE", "THORN"}, // Ю - uppercase THORN, Icelandic
        {"\u00DF", "szlig"}, // Я - lowercase sharps, German
        {"\u00E0", "agrave"}, // а - lowercase a, grave accent
        {"\u00E1", "aacute"}, // б - lowercase a, acute accent
        {"\u00E2", "acirc"}, // в - lowercase a, circumflex accent
        {"\u00E3", "atilde"}, // г - lowercase a, tilde
        {"\u00E4", "auml"}, // д - lowercase a, umlaut
        {"\u00E5", "aring"}, // е - lowercase a, ring
        {"\u00E6", "aelig"}, // ж - lowercase ae
        {"\u00E7", "ccedil"}, // з - lowercase c, cedilla
        {"\u00E8", "egrave"}, // и - lowercase e, grave accent
        {"\u00E9", "eacute"}, // й - lowercase e, acute accent
        {"\u00EA", "ecirc"}, // к - lowercase e, circumflex accent
        {"\u00EB", "euml"}, // л - lowercase e, umlaut
        {"\u00EC", "igrave"}, // м - lowercase i, grave accent
        {"\u00ED", "iacute"}, // н - lowercase i, acute accent
        {"\u00EE", "icirc"}, // о - lowercase i, circumflex accent
        {"\u00EF", "iuml"}, // п - lowercase i, umlaut
        {"\u00F0", "eth"}, // р - lowercase eth, Icelandic
        {"\u00F1", "ntilde"}, // с - lowercase n, tilde
        {"\u00F2", "ograve"}, // т - lowercase o, grave accent
        {"\u00F3", "oacute"}, // у - lowercase o, acute accent
        {"\u00F4", "ocirc"}, // ф - lowercase o, circumflex accent
        {"\u00F5", "otilde"}, // х - lowercase o, tilde
        {"\u00F6", "ouml"}, // ц - lowercase o, umlaut
        {"\u00F7", "divide"}, // division sign
        {"\u00F8", "oslash"}, // ш - lowercase o, slash
        {"\u00F9", "ugrave"}, // щ - lowercase u, grave accent
        {"\u00FA", "uacute"}, // ъ - lowercase u, acute accent
        {"\u00FB", "ucirc"}, // ы - lowercase u, circumflex accent
        {"\u00FC", "uuml"}, // ь - lowercase u, umlaut
        {"\u00FD", "yacute"}, // э - lowercase y, acute accent
        {"\u00FE", "thorn"}, // ю - lowercase thorn, Icelandic
        {"\u00FF", "yuml"}, // я - lowercase y, umlaut
    };

    private static final int MIN_ESCAPE = 2;
    private static final int MAX_ESCAPE = 6;

    private static final HashMap<String, CharSequence> lookupMap;
    static {
        lookupMap = new HashMap<String, CharSequence>();
        for (final CharSequence[] seq : ESCAPES) 
            lookupMap.put(seq[1].toString(), seq[0]);
    }

}

Di recente, ho dovuto ottimizzare un progetto Struts lento. Si è scoperto che sotto la copertina Struts chiama Apache per la fuga di stringhe html di default ( <s:property value="..."/>). Disattivando l'escaping ( <s:property value="..." escaping="false"/>) alcune pagine sono state eseguite dal 5% al ​​20% più velocemente.
Stephan,

In seguito ho scoperto che questo codice può entrare in loop quando viene fornita una stringa vuota come argomento. L'edizione corrente ha risolto questo problema.
Nick Frolov,

Questo sfugge o non spazio? & amp; non è decodificato. Solo e viene aggiunto alla mappa, quindi funziona solo in un modo?
mmm

3
Un StringWriter utilizza internamente un StringBuffer che utilizza il blocco. L'uso diretto di StringBuilder dovrebbe essere più veloce.
Axel Dörfler,

4
@NickFrolov, i tuoi commenti sembrano un po 'incasinati. aumlè per esempio äe no д.
aioobe,

12

La seguente libreria può essere utilizzata anche per l'escaping HTML in Java: unbescape .

L'HTML può essere scaricato in questo modo:

final String unescapedText = HtmlEscape.unescapeHtml(escapedText); 

2
Non ha fatto nulla per questo:%3Chtml%3E%0D%0A%3Chead%3E%0D%0A%3Ctitle%3Etest%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%3E%0D%0Atest%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E
ThreaT

40
@ThreaT Il tuo testo non è codificato in HTML, è codificato in URL.
Mikhail Batcer,

9

Questo ha fatto il lavoro per me,

import org.apache.commons.lang.StringEscapeUtils;
...
String decodedXML= StringEscapeUtils.unescapeHtml(encodedXML);

o

import org.apache.commons.lang3.StringEscapeUtils;
...
String decodedXML= StringEscapeUtils.unescapeHtml4(encodedXML);

Immagino sia sempre meglio usarlo lang3per ovvi motivi. Spero che questo ti aiuti :)


4

Una soluzione molto semplice ma inefficiente senza alcuna libreria esterna è:

public static String unescapeHtml3( String str ) {
    try {
        HTMLDocument doc = new HTMLDocument();
        new HTMLEditorKit().read( new StringReader( "<html><body>" + str ), doc, 0 );
        return doc.getText( 1, doc.getLength() );
    } catch( Exception ex ) {
        return str;
    }
}

Questo dovrebbe essere usato solo se hai solo un piccolo numero di string da decodificare.


1
Molto vicino, ma non esatto: ha convertito "qwAS12ƷƸDžǚǪǼȌ" in "qwAS12ƷƸDžǚǪǼȌ \ n".
Greg,

3

Il modo più affidabile è con

String cleanedString = StringEscapeUtils.unescapeHtml4(originalString);

da org.apache.commons.lang3.StringEscapeUtils.

E per sfuggire agli spazi bianchi

cleanedString = cleanedString.trim();

Ciò garantirà che gli spazi bianchi dovuti alla copia e incolla nei moduli Web non vengano mantenuti nel DB.


1

Spring Framework HtmlUtils

Se stai già utilizzando Spring Framework, usa il seguente metodo:

import static org.springframework.web.util.HtmlUtils.htmlUnescape;

...

String result = htmlUnescape(source);

0

Prendi in considerazione l'utilizzo della classe Java HtmlManipulator . Potrebbe essere necessario aggiungere alcuni elementi (non tutte le entità sono nell'elenco).

L'Apache Commons StringEscapeUtils come suggerito da Kevin Hakanson non ha funzionato al 100% per me; diverse entità come & # 145 (virgoletta singola sinistra) sono state tradotte in qualche modo in "222". Ho anche provato org.jsoup e ho avuto lo stesso problema.


0

Nel mio caso uso il metodo di sostituzione testando ogni entità in ogni variabile, il mio codice è simile al seguente:

text = text.replace("&Ccedil;", "Ç");
text = text.replace("&ccedil;", "ç");
text = text.replace("&Aacute;", "Á");
text = text.replace("&Acirc;", "Â");
text = text.replace("&Atilde;", "Ã");
text = text.replace("&Eacute;", "É");
text = text.replace("&Ecirc;", "Ê");
text = text.replace("&Iacute;", "Í");
text = text.replace("&Ocirc;", "Ô");
text = text.replace("&Otilde;", "Õ");
text = text.replace("&Oacute;", "Ó");
text = text.replace("&Uacute;", "Ú");
text = text.replace("&aacute;", "á");
text = text.replace("&acirc;", "â");
text = text.replace("&atilde;", "ã");
text = text.replace("&eacute;", "é");
text = text.replace("&ecirc;", "ê");
text = text.replace("&iacute;", "í");
text = text.replace("&ocirc;", "ô");
text = text.replace("&otilde;", "õ");
text = text.replace("&oacute;", "ó");
text = text.replace("&uacute;", "ú");

Nel mio caso ha funzionato molto bene.


2
Questa non è ogni entità speciale. Persino i due citati nella domanda mancano.
Sandy Gifford,

questo non si ridimensionerà bene
denov

-7

Nel caso in cui desideri imitare quale funzione php htmlspecialchars_decode utilizza la funzione php get_html_translation_table () per scaricare la tabella e quindi utilizzare il codice java come,

static Map<String,String> html_specialchars_table = new Hashtable<String,String>();
static {
        html_specialchars_table.put("&lt;","<");
        html_specialchars_table.put("&gt;",">");
        html_specialchars_table.put("&amp;","&");
}
static String htmlspecialchars_decode_ENT_NOQUOTES(String s){
        Enumeration en = html_specialchars_table.keys();
        while(en.hasMoreElements()){
                String key = en.nextElement();
                String val = html_specialchars_table.get(key);
                s = s.replaceAll(key, val);
        }
        return s;
}

7
Non lanciare così tanto; usa generici su quella HashMap! Inoltre, usa un foreach, non un po 'per iterarlo, il codice apparirà molto più leggibile!
WhyNotHugo,

3
@BalaDutt se migliorerai la tua risposta, i ragazzi ti daranno punti :)
sparkyspider

3
Migliora anche la tua funzione e i nomi delle variabili, @Bala.
Thomas W,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.