Decodifica e amp; torna a & in JavaScript


230

Ho delle stringhe come

var str = 'One & two & three';

reso in HTML dal web server. Ho bisogno di trasformare quelle stringhe in

'One & two & three'

Attualmente, è quello che sto facendo (con l'aiuto di jQuery):

$(document.createElement('div')).html('{{ driver.person.name }}').text()

Tuttavia ho la sensazione inquietante di sbagliare. Ho provato

unescape("&")

ma non sembra funzionare, né decomponeURI / decodeURIComponent.

Ci sono altri modi più nativi ed eleganti per farlo?


L'enorme funzione inclusa in questo articolo sembra funzionare bene: blogs.msdn.com/b/aoakley/archive/2003/11/12/49645.aspx Non penso che sia la soluzione più intelligente ma funziona.
Matias,

1
Poiché le stringhe contenenti entità HTML sono qualcosa di diverso dalle stringhe codificateescape d o URI , tali funzioni non funzioneranno.
Marcel Korpel,

1
@Matias nota che nuove entità con nome sono state aggiunte all'HTML (ad es. Tramite la specifica HTML 5) da quando quella funzione è stata creata nel 2003 - ad esempio, non riconosce 𝕫. Questo è un problema con una specifica in evoluzione; come tale, dovresti scegliere uno strumento che viene effettivamente gestito per risolverlo.
Mark Amery,

1
@MarkAmery sì, sono totalmente d'accordo! È una bella esperienza tornare a queste domande dopo un paio d'anni, grazie!
Matias,

Risposte:


105

Un'opzione più moderna per interpretare HTML (testo e non) da JavaScript è il supporto HTML DOMParsernell'API ( vedi qui in MDN ). Ciò consente di utilizzare il parser HTML nativo del browser per convertire una stringa in un documento HTML. È stato supportato nelle nuove versioni di tutti i principali browser dalla fine del 2014.

Se vogliamo solo decodificare del contenuto del testo, possiamo inserirlo come unico contenuto in un corpo del documento, analizzare il documento ed estrarne il contenuto .body.textContent.

var encodedStr = 'hello & world';

var parser = new DOMParser;
var dom = parser.parseFromString(
    '<!doctype html><body>' + encodedStr,
    'text/html');
var decodedString = dom.body.textContent;

console.log(decodedString);

Possiamo vedere nella bozza delle specificheDOMParser che JavaScript non è abilitato per il documento analizzato, quindi possiamo eseguire questa conversione di testo senza problemi di sicurezza.

Il parseFromString(str, type)metodo deve eseguire questi passaggi, a seconda del tipo :

  • "text/html"

    Analizzare str con an HTML parsere restituire il nuovo creato Document.

    Il flag di scripting deve essere impostato su "disabilitato".

    NOTA

    scriptgli elementi vengono contrassegnati come non eseguibili e il contenuto di noscriptviene analizzato come markup.

È al di là dell'ambito di questa domanda, ma tieni presente che se stai prendendo i nodi DOM analizzati (non solo il loro contenuto di testo) e li sposti nel DOM del documento live, è possibile che i loro script vengano riattivati ​​e potrebbero essere problemi di sicurezza. Non l'ho studiato, quindi per favore fai attenzione.


5
qualche alternativa per NodeJs?
coderInrain

285

Devi decodificare tutte le entità HTML codificate o solo &amp;se stesso?

Se devi solo gestirlo &amp;, puoi farlo:

var decoded = encoded.replace(/&amp;/g, '&');

Se devi decodificare tutte le entità HTML, puoi farlo senza jQuery:

var elem = document.createElement('textarea');
elem.innerHTML = encoded;
var decoded = elem.value;

Si prega di prendere nota dei commenti di Mark in basso che evidenziano le falle di sicurezza in una versione precedente di questa risposta e raccomandano di utilizzare textareaanziché divmitigare le potenziali vulnerabilità XSS. Queste vulnerabilità esistono se si utilizza jQuery o JavaScript semplice.


16
Attenzione! Questo è potenzialmente insicuro. In encoded='<img src="bla" onerror="alert(1)">'tal caso il frammento sopra mostrerà un avviso. Ciò significa che se il testo codificato proviene dall'input dell'utente, decodificarlo con questo frammento può presentare una vulnerabilità XSS.
Mark Amery,

@MarkAmery Non sono un esperto di sicurezza, ma sembra che se imposti immediatamente il div nulldopo aver ricevuto il testo, l'avviso nell'img
jsfiddle.net/Mottie/gaBeb/128

4
@Mottie nota con certezza in quale browser ha funzionato per te, ma alert(1)mi spara ancora su Chrome su OS X. Se vuoi una variante sicura di questo hack, prova a usare atextarea .
Mark Amery,

+1 per la semplice regexp sostituisce l'alternativa per un solo tipo di entità html. Usalo se ti aspetti che i dati html vengano interpolati, ad esempio, da un'app di pallone in pitone a un modello.
OzzyTheGiant

Come fare questo sul server Node?
Mohammad Kermani,

44

Matthias Bynens ha una libreria per questo: https://github.com/mathiasbynens/he

Esempio:

console.log(
    he.decode("J&#246;rg &amp J&#xFC;rgen rocked to &amp; fro ")
);
// Logs "Jörg & Jürgen rocked to & fro"

Suggerisco di favorirlo rispetto agli hack che implicano l'impostazione del contenuto HTML di un elemento e la lettura del suo contenuto testuale. Tali approcci possono funzionare, ma sono ingannevolmente pericolosi e presentano opportunità XSS se utilizzati su input dell'utente non attendibile.

Se davvero non riesci a caricare in una libreria, puoi usare l' textareahack descritto in questa risposta a una domanda quasi duplicata, che, a differenza di vari approcci simili che sono stati suggeriti, non ha buchi di sicurezza che io conosca:

function decodeEntities(encodedString) {
    var textArea = document.createElement('textarea');
    textArea.innerHTML = encodedString;
    return textArea.value;
}

console.log(decodeEntities('1 &amp; 2')); // '1 & 2'

Ma prendi nota dei problemi di sicurezza, che riguardano approcci simili a questo, che elencherò nella risposta collegata! Questo approccio è un hack e future modifiche al contenuto consentito di uno textarea(o bug in particolari browser) potrebbero portare a un codice che si affida all'improvviso un buco XSS un giorno.


La biblioteca di Matthias Bynens heè assolutamente fantastica! Grazie mille per la raccomandazione!
Pedro A

23
var htmlEnDeCode = (function() {
    var charToEntityRegex,
        entityToCharRegex,
        charToEntity,
        entityToChar;

    function resetCharacterEntities() {
        charToEntity = {};
        entityToChar = {};
        // add the default set
        addCharacterEntities({
            '&amp;'     :   '&',
            '&gt;'      :   '>',
            '&lt;'      :   '<',
            '&quot;'    :   '"',
            '&#39;'     :   "'"
        });
    }

    function addCharacterEntities(newEntities) {
        var charKeys = [],
            entityKeys = [],
            key, echar;
        for (key in newEntities) {
            echar = newEntities[key];
            entityToChar[key] = echar;
            charToEntity[echar] = key;
            charKeys.push(echar);
            entityKeys.push(key);
        }
        charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
        entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
    }

    function htmlEncode(value){
        var htmlEncodeReplaceFn = function(match, capture) {
            return charToEntity[capture];
        };

        return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
    }

    function htmlDecode(value) {
        var htmlDecodeReplaceFn = function(match, capture) {
            return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
        };

        return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
    }

    resetCharacterEntities();

    return {
        htmlEncode: htmlEncode,
        htmlDecode: htmlDecode
    };
})();

Questo proviene dal codice sorgente di ExtJS.


4
-1; questo non riesce a gestire la stragrande maggioranza delle entità nominate. Ad esempio, htmlEnDecode.htmlDecode('&euro;')dovrebbe tornare '€', ma invece restituisce '&euro;'.
Mark Amery,


15

È possibile utilizzare la funzione unescape / escape di Lodash https://lodash.com/docs/4.17.5#unescape

import unescape from 'lodash/unescape';

const str = unescape('fred, barney, &amp; pebbles');

lo diventerà 'fred, barney, & pebbles'


1
probabilmente meglio fare "importare _unescape da 'lodash / unescape';" quindi non è in conflitto con la funzione javascript obsoleta con lo stesso nome: unescape
Rick Penabella,

14

Nel caso tu lo stia cercando, come me - nel frattempo c'è un metodo JQuery bello e sicuro.

https://api.jquery.com/jquery.parsehtml/

Puoi f.ex. digita questo nella tua console:

var x = "test &amp;";
> undefined
$.parseHTML(x)[0].textContent
> "test &"

Quindi $ .parseHTML (x) restituisce un array, e se hai un markup HTML nel tuo testo, array.length sarà maggiore di 1.


Ha funzionato perfettamente per me, questo era esattamente quello che stavo cercando, grazie.
Jonathan Nielsen,

1
Se xha un valore di <script>alert('hello');</script>quanto sopra andrà in crash. Nell'attuale jQuery in realtà non tenterà di eseguire lo script, ma [0]cederà in undefinedmodo che la chiamata textContentfallisca e il tuo script si fermerà lì. $('<div />').html(x).text();sembra più sicuro - via gist.github.com/jmblog/3222899
Andrew Hodgkinson

@AndrewHodgkinson sì, ma la domanda era "Decodifica & amp; torna a & in JavaScript" - quindi testerai prima il contenuto di x o ti assicurerai di usarlo solo nei casi corretti.
cslotty,

Non vedo davvero come segue. Il codice sopra funziona in tutti i casi. E come esattamente "assicureresti" che il valore di x avesse bisogno di essere riparato? E se l'esempio di script sopra riportato avvisasse "& amp;" così che aveva davvero bisogno di correzioni? Non abbiamo idea da dove provengano le stringhe del PO, quindi è necessario prendere in considerazione input dannosi.
Andrew Hodgkinson,

@AndrewHodgkinson Mi piace la tua considerazione, ma non è questa la domanda. Sentiti libero di rispondere a questa domanda, però. Suppongo che potresti rimuovere i tag di script, f.ex.
cslotty,

8

jQuery codificherà e decodificherà per te. Tuttavia, è necessario utilizzare un tag textarea, non un div.

var str1 = 'One & two & three';
var str2 = "One &amp; two &amp; three";
  
$(document).ready(function() {
   $("#encoded").text(htmlEncode(str1)); 
   $("#decoded").text(htmlDecode(str2));
});

function htmlDecode(value) {
  return $("<textarea/>").html(value).text();
}

function htmlEncode(value) {
  return $('<textarea/>').text(value).html();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<div id="encoded"></div>
<div id="decoded"></div>


2
-1 perché c'è un buco di sicurezza (sorprendente) qui per le vecchie versioni di jQuery, alcune delle quali probabilmente hanno ancora una base di utenti significativa - tali versioni rileveranno e valuteranno esplicitamente gli script nel codice HTML passati .html(). Quindi anche l'uso di a textareanon è sufficiente per garantire la sicurezza qui; Suggerisco di non utilizzare jQuery per questa attività e di scrivere codice equivalente con la semplice API DOM . (Sì, quel vecchio comportamento di jQuery è pazzo e terribile.)
Mark Amery,

Grazie per averlo sottolineato. Tuttavia, la domanda non include l'obbligo di verificare l'iniezione di script. La domanda si pone specificamente sull'html reso dal web server. Il contenuto HTML salvato su un server Web dovrebbe probabilmente essere convalidato per l'iniezione di script prima del salvataggio.
Jason Williams,

4

Per prima cosa crea un <span id="decodeIt" style="display:none;"></span>posto nel corpo

Quindi, assegnare la stringa da decodificare come innerHTML a questo:

document.getElementById("decodeIt").innerHTML=stringtodecode

Finalmente,

stringtodecode=document.getElementById("decodeIt").innerText

Ecco il codice generale:

var stringtodecode="<B>Hello</B> world<br>";
document.getElementById("decodeIt").innerHTML=stringtodecode;
stringtodecode=document.getElementById("decodeIt").innerText

1
-1; questo è pericolosamente insicuro da usare su input non attendibili. Ad esempio, considera cosa succede se stringtodecodecontiene qualcosa di simile <script>alert(1)</script>.
Mark Amery,

2

una soluzione javascript che cattura quelli comuni:

var map = {amp: '&', lt: '<', gt: '>', quot: '"', '#039': "'"}
str = str.replace(/&([^;]+);/g, (m, c) => map[c])

questo è il contrario di https://stackoverflow.com/a/4835406/2738039


Se usi map[c] || ''quelli non riconosciuti non verranno mostrati comeundefined
Eldelshell il

Copertura molto limitata; -1.
Mark Amery,

2
+1, di più èunescapeHtml(str){ var map = {amp: '&', lt: '<', le: '≤', gt: '>', ge: '≥', quot: '"', '#039': "'"} return str.replace(/&([^;]+);/g, (m, c) => map[c]|| '') }
Trần Quốc Hoài nuovo 2015

Copertura manuale. Non consigliato.
Sergio A.

2

Per i ragazzi di una riga:

const htmlDecode = innerHTML => Object.assign(document.createElement('textarea'), {innerHTML}).value;

console.log(htmlDecode('Complicated - Dimitri Vegas &amp; Like Mike'));

2

La domanda non specifica l'origine, xma ha senso difendere, se possibile, da input dannosi (o semplicemente inattesi, dalla nostra stessa applicazione). Ad esempio, supponiamo che xabbia un valore di &amp; <script>alert('hello');</script>. Un modo semplice e sicuro per gestirlo in jQuery è:

var x    = "&amp; <script>alert('hello');</script>";
var safe = $('<div />').html(x).text();

// => "& alert('hello');"

Trovato tramite https://gist.github.com/jmblog/3222899 . Non riesco a vedere molte ragioni per evitare di usare questa soluzione dato che è almeno altrettanto breve, se non più breve di alcune alternative e fornisce difesa contro XSS.

(Inizialmente l'ho pubblicato come commento, ma lo sto aggiungendo come risposta poiché un successivo commento nella stessa discussione mi ha richiesto di farlo).


1

Ho provato di tutto per rimuovere e da un array JSON. Nessuno degli esempi precedenti, ma https://stackoverflow.com/users/2030321/chris ha fornito un'ottima soluzione che mi ha portato a risolvere il mio problema.

var stringtodecode="<B>Hello</B> world<br>";
document.getElementById("decodeIt").innerHTML=stringtodecode;
stringtodecode=document.getElementById("decodeIt").innerText

Non ho usato, perché non ho capito come inserirlo in una finestra modale che stava tirando i dati JSON in un array, ma ho provato questo basato sull'esempio e ha funzionato:

var modal = document.getElementById('demodal');
$('#ampersandcontent').text(replaceAll(data[0],"&amp;", "&"));

Mi piace perché era semplice e funziona, ma non so perché non sia ampiamente usato. Ho cercato ciao e in basso per trovare una soluzione semplice. Continuo a cercare la comprensione della sintassi e se esiste qualche rischio nell'usarlo. Non ho ancora trovato nulla.


La tua prima proposta è solo un po 'complicata, ma funziona bene senza troppi sforzi. Il secondo, d'altra parte, usa solo la forza bruta per decodificare i personaggi; ciò significa che potrebbero essere necessari MOLTO sforzo e tempo per realizzare una funzione di decodifica completa. Ecco perché nessuno sta usando quel modo per risolvere il problema di OP.
Sergio A.
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.