Расшифровать & amp; вернуться к & в JavaScript


230

У меня есть такие строки, как

var str = 'One & two & three';

визуализируется в HTML веб-сервером. Мне нужно преобразовать эти строки в

'One & two & three'

В настоящее время это то, что я делаю (с помощью jQuery):

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

Однако у меня тревожное чувство, что я делаю это неправильно. я пытался

unescape("&")

но это, похоже, не работает, как и decodeURI / decodeURIComponent.

Есть ли другие, более родные и элегантные способы сделать это?


Кажется, что огромная функция, включенная в эту статью, работает нормально: blogs.msdn.com/b/aoakley/archive/2003/11/12/49645.aspx Я не думаю, что это самое умное решение, но оно работает.
Матиас

1
Поскольку строки, содержащие сущности HTML, отличаются от строк в кодировкеescape d или URI , эти функции работать не будут.
Марсель Корпель

1
@Matias отмечает, что новые именованные объекты были добавлены в HTML (например, через спецификацию HTML 5) с тех пор, как эта функция была создана в 2003 году - например, она не распознает 𝕫. Это проблема с развивающейся спецификацией; как таковой, вы должны выбрать инструмент, который на самом деле поддерживается, чтобы решить его с помощью.
Марк Эмери

1
@MarkAmery да, я полностью согласен! Это хороший опыт, чтобы вернуться к этим вопросам через пару лет, спасибо!
Матиас

Ответы:


105

Более современным вариантом для интерпретации HTML (текста и других) из JavaScript является поддержка HTML в DOMParserAPI ( см. Здесь в MDN). ). Это позволяет использовать собственный анализатор HTML браузера для преобразования строки в документ HTML. Он поддерживается в новых версиях всех основных браузеров с конца 2014 года.

Если мы просто хотим декодировать некоторый текстовый контент, мы можем поместить его в качестве единственного содержимого в теле документа, проанализировать документ и извлечь его .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);

Мы можем видеть в проекте спецификации,DOMParser что JavaScript не включен для проанализированного документа, поэтому мы можем выполнить это преобразование текста без проблем с безопасностью.

parseFromString(str, type)Метод должен выполнить следующие действия, в зависимости от типа :

  • "text/html"

    Parse str с HTML parserи вернуть только что созданноеDocument .

    Флаг сценария должен быть установлен на «отключен».

    НОТА

    scriptэлементы помечаются как неисполняемые, а содержимое noscriptразбирается как разметка.

Этот вопрос выходит за рамки этого вопроса, но, пожалуйста , обратите внимание, что если вы берете проанализированные узлы DOM (а не только их текстовое содержимое) и перемещаете их в DOM документа, работающего в режиме реального времени, возможно, что их сценарии будут повторно включены, и может быть проблемы безопасности. Я не исследовал это, поэтому, пожалуйста, будьте осторожны.


5
любая альтернатива для NodeJs?
coderInrRain

285

Вам нужно декодировать все закодированные сущности HTML или только &amp;себя?

Если вам нужно только обработать, &amp;то вы можете сделать это:

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

Если вам нужно декодировать все сущности HTML, вы можете сделать это без jQuery:

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

Пожалуйста, обратите внимание на комментарии Марка ниже, которые выделяют дыры в безопасности в более ранней версии этого ответа и рекомендуют использовать, textareaа не divсмягчать против потенциальных уязвимостей XSS. Эти уязвимости существуют независимо от того, используете ли вы jQuery или простой JavaScript.


16
Осторожно! Это потенциально небезопасно. Если encoded='<img src="bla" onerror="alert(1)">'тогда фрагмент выше покажет предупреждение. Это означает, что если ваш закодированный текст поступает от пользователя, его декодирование с помощью этого фрагмента может представлять уязвимость XSS.
Марк Амери

@MarkAmery Я не эксперт по безопасности, но похоже, что если сразу nullпосле получения текста установить div , оповещение в img не сработает
jsfiddle.net/Mottie/gaBeb/128

4
@ Обратите внимание, что браузер, который у вас работал, alert(1)все еще работает для меня в Chrome на OS X. Если вы хотите безопасный вариант этого хака, попробуйте использоватьtextarea .
Марк Амери

+1 для простого регулярного выражения заменить альтернативу только для одного вида HTML-сущности. Используйте это, если вы ожидаете, что html-данные будут интерполированы, скажем, из приложения Python-флаконов в шаблон.
OzzyTheGiant

Как это сделать на сервере Node?
Мохаммад Кермани

44

У Матиаса Биненса есть библиотека для этого: https://github.com/mathiasbynens/he

Пример:

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

Я предпочитаю отдавать предпочтение этому хаку, включая установку HTML-содержимого элемента и последующее чтение его текстового содержимого. Такие подходы могут работать, но они обманчиво опасны и представляют возможности XSS, если они используются для ненадежного пользовательского ввода.

Если вы действительно не можете загрузить библиотеку, вы можете использовать textareaхак, описанный в этом ответе, для почти дублированного вопроса, который, в отличие от предложенных различных похожих подходов, не имеет дыр в безопасности, о которых я знаю:

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

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

Но обратите внимание на проблемы безопасности, затрагивающие сходные подходы к этому, которые я перечислю в связанном ответе! Этот подход является хакерским, и будущие изменения в допустимом содержимом textarea(или ошибках в определенных браузерах) могут привести к тому, что код, который полагается на него, однажды вдруг обнаружит дыру в XSS.


Библиотека Матиаса Биненса heпросто великолепна! Большое спасибо за рекомендацию!
Педро А

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
    };
})();

Это из исходного кода ExtJS.


4
-1; это не в состоянии справиться с подавляющим большинством именованных организаций. Например, htmlEnDecode.htmlDecode('&euro;')должен вернуться '€', но вместо этого возвращается '&euro;'.
Марк Амери


15

Вы можете использовать функцию unescape / escape Lodash https://lodash.com/docs/4.17.5#unescape

import unescape from 'lodash/unescape';

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

ул станет 'fred, barney, & pebbles'


1
вероятно, лучше сделать "импорт _unescape из 'lodash / unescape';" так что это не противоречит устаревшей функции javascript с тем же именем:
unescape

14

В случае, если вы ищете его, как я - тем временем есть хороший и безопасный метод JQuery.

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

Вы можете, например, введите это в вашей консоли:

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

Таким образом, $ .parseHTML (x) возвращает массив, и если в вашем тексте есть разметка HTML, то значение array.length будет больше 1.


Отлично сработало для меня, это было именно то, что я искал, спасибо.
Джонатан Нильсен

1
Если xимеет значение <script>alert('hello');</script>выше, произойдет сбой. В текущем jQuery он на самом деле не будет пытаться запустить скрипт, но [0]даст результат, undefinedтак что вызов textContentзавершится неудачно, и ваш скрипт остановится на этом. $('<div />').html(x).text();выглядит безопаснее - через gist.github.com/jmblog/3222899
Эндрю Ходжкинсон

@AndrewHodgkinson да, но вопрос был «Расшифровать и обратно в JavaScript» - так что вы сначала протестируете содержимое x или убедитесь, что используете его только в правильных случаях.
cslotty

Я действительно не вижу, как это следует. Код выше работает во всех случаях. И как именно вы «убедитесь», что значение х необходимо исправить? А что если в приведенном выше примере скрипта появилось предупреждение & amp; так что это действительно нужно исправить? Мы понятия не имеем, откуда берутся строки OP, поэтому необходимо учитывать злонамеренный ввод.
Эндрю Ходжкинсон

@AndrewHodgkinson Мне нравится ваше мнение, но здесь вопрос не в этом. Не стесняйтесь ответить на этот вопрос, хотя. Я думаю, вы могли бы удалить теги сценария, например.
cslotty

8

JQuery будет кодировать и декодировать для вас. Однако вам нужно использовать тег textarea, а не 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 потому что здесь есть (удивительная) дыра в безопасности для старых версий jQuery, некоторые из которых, вероятно, все еще имеют значительную пользовательскую базу - эти версии будут обнаруживать и явно оценивать скрипты в передаваемом HTML-коде .html(). Таким образом, даже использования textareaнедостаточно для обеспечения безопасности здесь; Я предлагаю не использовать jQuery для этой задачи и писать эквивалентный код с простым API DOM . (Да, это старое поведение jQuery безумно и ужасно.)
Марк Эмери

Спасибо, что указали на это. Тем не менее, вопрос не включает в себя требование проверять наличие скриптов. Вопрос, в частности, задается относительно HTML, отображаемого веб-сервером. Содержание HTML, сохраненное на веб-сервере, вероятно, должно быть проверено на наличие скриптов перед сохранением.
Джейсон Уильямс

4

Сначала создайте <span id="decodeIt" style="display:none;"></span>где-нибудь в теле

Затем присвойте строку, которая будет декодирована как innerHTML:

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

В заключение,

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

Вот общий код:

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

1
-1; это опасно небезопасно для использования на ненадежных данных. Например, рассмотрим, что произойдет, если stringtodecodeсодержит что-то вроде <script>alert(1)</script>.
Марк Амери

2

решение JavaScript, которое ловит общие:

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

это обратная сторона https://stackoverflow.com/a/4835406/2738039


Если вы используете map[c] || ''нераспознанные, они не будут отображаться какundefined
Eldelshell

Очень ограниченный охват; -1.
Марк Амери

2
+1, больше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 new 2015

Ручное покрытие. Не рекомендуется.
Серхио А.

2

Для однострочных парней:

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

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

2

Вопрос не определяет происхождение, xно имеет смысл защищать, если мы можем, от злонамеренного (или просто неожиданного, из нашего собственного приложения) ввода. Например, предположим, xимеет значение &amp; <script>alert('hello');</script>. Безопасный и простой способ справиться с этим в jQuery:

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

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

Найдено через https://gist.github.com/jmblog/3222899 . Я не вижу много причин, чтобы избегать использования этого решения, поскольку оно, по крайней мере, такое же короткое, если не короче, чем некоторые альтернативы и обеспечивает защиту от XSS.

(Первоначально я разместил это как комментарий, но добавляю его как ответ, поскольку последующий комментарий в той же ветке попросил меня сделать это).


1

Я попробовал все, чтобы удалить & из массива JSON. Ни один из приведенных выше примеров, но https://stackoverflow.com/users/2030321/chris не дал отличного решения, которое привело меня к решению моей проблемы.

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

Я не использовал, потому что я не понимал, как вставить его в модальное окно, которое вытягивало данные JSON в массив, но я попробовал это на основе примера, и это сработало:

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

Мне нравится это, потому что это было просто, и это работает, но не уверен, почему это не широко используется. Искал привет и низ, чтобы найти простое решение. Я продолжаю искать понимание синтаксиса и есть ли риск его использования. Пока не нашли ничего.


Ваше первое предложение немного сложнее, но оно работает без особых усилий. Второй, с другой стороны, использует только грубую силу для декодирования символов; это означает, что для выполнения полной функции декодирования может потребоваться МНОГО усилий и времени. Вот почему никто не использует этот способ для решения проблемы ОП.
Серхио А.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.