Розшифрувати & підсилювач; повернутися до & в JavaScript


229

У мене є такі струни

var str = 'One & two & three';

переведений у HTML веб-сервером. Мені потрібно перетворити ці рядки в

'One & two & three'

Наразі це я роблю (за допомогою jQuery):

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

Однак у мене є неспокійне відчуття, що я роблю це неправильно. я намагався

unescape("&")

але це, здається, не працює, також не декодуютьURI / 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 так, я повністю згоден! Приємний досвід повернутися до цих питань через пару років, дякую!
Матіас

Відповіді:


104

Більш сучасним варіантом інтерпретації 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"

    Проаналізуйте str з an HTML parserі поверніть новостворене Document.

    Сценарій прапор повинен бути встановлений на "відключений".

    ПРИМІТКА

    scriptелементи позначаються невиконаними, а вміст noscriptрозбирається як розмітка.

Це не виходить за рамки цього питання, але зауважте, що якщо ви берете самі розібрані вузли DOM (не лише їх текстовий вміст) і переміщуєте їх до прямого документа DOM, можливо, їх сценарій буде відновлено, і це може бути бути питаннями безпеки. Я цього не досліджував, тому будьте обережні.


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

284

Вам потрібно розшифрувати всі закодовані 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після отримання тексту, попередження в малюнку
jsfiddle.net/Mottie/gaBeb/128

4
@Mottie зауважте, у якому браузері працював для вас, але 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');

str стане 'fred, barney, & pebbles'


1
ймовірно, краще зробити "import _unescape з 'lodash / unescape"; " тож це не суперечить
устареній

14

Якщо ви шукаєте його, як я - тим часом є приємний і безпечний метод JQuery.

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

Ви можете f.ex. введіть це у консоль:

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;" так що вона справді потребувала корекції? Ми не маємо ідеї, звідки беруться рядки ОП, тому слід враховувати зловмисне введення.
Ендрю Ходжкінсон

@AndrewHodgkinson Мені подобається ваш розгляд, але тут не в цьому питання. Але не соромтесь відповісти на це питання. Я думаю, ви можете видалити теги сценаріїв, f.ex.
cslotty

8

jQuery буде кодувати та декодувати для вас. Однак вам потрібно використовувати тег textarea, а не діл.

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>десь у тілі

Далі призначте для цього рядок, який потрібно декодувати як внутрішній HTML:

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 новий 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.