як уникнути сутностей xml у javascript?


78

У JavaScript (nodejs на стороні сервера) я пишу програму, яка генерує xml як результат.

Я будую xml, об'єднуючи рядок:

str += '<' + key + '>';
str += value;
str += '</' + key + '>';

Проблема в тому , що , якщо valueмістить символи , такі як '&', '>'або'<' ? Який найкращий спосіб уникнути цих персонажів?

чи існує якась бібліотека javascript, навколо якої можуть уникнути сутності XML?

Відповіді:


119

Кодування HTML просто замінюючи &, ", ', <і >символи з їх еквівалентами сутностей. Порядок має значення, якщо спочатку ви не заміните &символи, ви подвоїте кодування деяких об’єктів:

if (!String.prototype.encodeHTML) {
  String.prototype.encodeHTML = function () {
    return this.replace(/&/g, '&amp;')
               .replace(/</g, '&lt;')
               .replace(/>/g, '&gt;')
               .replace(/"/g, '&quot;')
               .replace(/'/g, '&apos;');
  };
}

Як зазначив @Johan BW de Vries, у цього будуть проблеми з назвами тегів, я хотів би пояснити, що я зробив припущення, що це було використано для value єдиного

І навпаки , якщо ви хочете , щоб розшифрувати HTML сутностей 1 , переконайтеся , що ви розшифрувати , &amp;щоб &після того, як все інше , так що ви не подвійний декодує будь-які об'єкти:

if (!String.prototype.decodeHTML) {
  String.prototype.decodeHTML = function () {
    return this.replace(/&apos;/g, "'")
               .replace(/&quot;/g, '"')
               .replace(/&gt;/g, '>')
               .replace(/&lt;/g, '<')
               .replace(/&amp;/g, '&');
  };
}

1 тільки основи, не включаючи &copy;в ©або інші подібні речі


Що стосується бібліотек. Underscore.js (або Lodash, якщо хочете) надає _.escapeметод для виконання цієї функціональності.


2
Це майже охоплює 5 сутностей XML. Просто потрібно @apos;
Райан

2
Це схоже на те, що він знову і знову замінює один і той же рядок, що може мати велику продуктивність при обробці великої кількості даних. Будь-яка швидша альтернатива?
Джонні

2
@Jonny, регулярний вираз забезпечить гіршу продуктивність, ніж багаторазові дзвінки .replace(). У будь-якому випадку вам доведеться мати серйозно величезну кількість даних, щоб помітити будь-які значущі проблеми. Швидшою альтернативою було б порівняти свій додаток і знайти фактичну точку задушення (зазвичай вкладені цикли), а не турбуватися про щось таке незначне, як це.
zzzzBov

1
У мене було 100-200 рядків даних у електронній таблиці Google. Я перетворював це на плісти (xml) і мав замінити ці сутності xml. Я написав власну функцію javascript, використовуючи для цього наведений вище код. Це працювало, але було дуже повільно. Електронна таблиця часом задихалася, але оскільки це просто крок "зробити один раз", швидкість зрештою не мала значення.
Джонні

4
Я знаю, що ця відповідь давня, але лише для того, щоб пояснити новачкам у JS: приєднання випадкових функцій, які не є полізаповненнями для деяких стандартизованих пропозицій, до глобальних прототипів - погана ідея.
austin_ce

104

Це може бути трохи ефективніше з однаковим результатом:

function escapeXml(unsafe) {
    return unsafe.replace(/[<>&'"]/g, function (c) {
        switch (c) {
            case '<': return '&lt;';
            case '>': return '&gt;';
            case '&': return '&amp;';
            case '\'': return '&apos;';
            case '"': return '&quot;';
        }
    });
}

Це здається кращим рішенням. Чому немає підйомів?
Віктор Граці,

1
@VictorGrazi: Ваше право, його в 49 із 50 тестують швидше рішення. Можливо, це тому, що вона майже на 5 років молодша за прийняту відповідь.
Себастьян,

1
@ Себастьян ах, це пояснювало б це, дякую. Подивіться, люди ^ ^ ^ ^ це рішення, яке ви хочете !!!
Віктор Граці,

Це мені здається кращим рішенням, ніж прийнята відповідь, яка проходить весь рядок п’ять разів (послідовно, зменшуючи сферу оптимізації двигуна JS), шукаючи збіг з одним символом; Рішення hgoebl проходить вхідний рядок лише один раз, намагаючись зіставити кожен символ з однією з п'яти умов. Питання в тому, що дорожче: 1) обхід рядка; або: 2) зіставлення кожного символу з 5 можливими символами. Я вважаю, що 1) буде більш дорогим.
Джеймі Бірч,

1
Декодування @RanLottem набагато складніше, якщо введення здійснюється у форматі HTML, див. Вікіпедія . Краще використовувати парсер (XML або документ).
hgoebl

21

Якщо у вас є jQuery, ось просте рішення:

  String.prototype.htmlEscape = function() {
    return $('<div/>').text(this.toString()).html();
  };

Використовуйте його так:

"<foo&bar>".htmlEscape(); -> "&lt;foo&amp;bar&gt"


1
Мені подобається ця техніка, оскільки вона має відношення "нехай це робить браузер". Чи є мінуси, можливо, крім гіршої продуктивності, оскільки це відбувається через DOM API?
avernet

$('<div/>').text('<&\'>"').html()"&lt;&amp;'&gt;""
Одинарної

1
Одинарні та подвійні лапки, як правило, не потрібно уникати.
lambshaanxy

7

Ви можете скористатися наведеним нижче способом. Я додав це в прототип для зручності доступу. Я також використовував негативний прогноз, тому він не заплутує речі, якщо ви викликаєте метод двічі або більше.

Використання:

 var original = "Hi&there";
 var escaped = original.EncodeXMLEscapeChars();  //Hi&amp;there

Декодування автоматично обробляється в синтаксичному аналізаторі XML.

Метод:

//String Extenstion to format string for xml content.
//Replces xml escape chracters to their equivalent html notation.
String.prototype.EncodeXMLEscapeChars = function () {
    var OutPut = this;
    if ($.trim(OutPut) != "") {
        OutPut = OutPut.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
        OutPut = OutPut.replace(/&(?!(amp;)|(lt;)|(gt;)|(quot;)|(#39;)|(apos;))/g, "&amp;");
        OutPut = OutPut.replace(/([^\\])((\\\\)*)\\(?![\\/{])/g, "$1\\\\$2");  //replaces odd backslash(\\) with even.
    }
    else {
        OutPut = "";
    }
    return OutPut;
};

3
Недооцінене відмінне рішення. Переконавшись, що ви не закінчите з сумнозвісним & amp; amp; рядок у вашому виведенні красивий.
Steve Westbrook

За допомогою цього коду ви щойно відредагували всі екземпляри рядка у всьому додатку, наприклад let a = 'foo', цей код вплине на нього. Краще створити допоміжну функцію замість розширення прототипу.
Лукас Ліесіс

Будь ласка, не мутуйте вбудовані об'єкти, оскільки це призводить до конфліктів, а це дуже погана практика.
slikts

1

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

   var escapeXml = (function() {
        var doc = document.implementation.createDocument("", "", null)
        var el = doc.createElement("temp");
        el.textContent = "temp";
        el = el.firstChild;
        var ser =  new XMLSerializer();
        return function(text) {
            el.nodeValue = text;
            return ser.serializeToString(el);
        };
    })();

console.log(escapeXml("<>&")); //&lt;&gt;&amp;

Це передбачає наявність у вас documentоб’єкта. У мене його немає.
Лукас Ліесіс


1

Обережно, всі регулярні вирази не є хорошими, якщо у вас XML всередині XML.
Натомість один раз прокрутіть рядок і підставте всі символи переходу.
Таким чином, ви не можете двічі наїхати на одного персонажа.

function _xmlAttributeEscape(inputString)
{
    var output = [];

    for (var i = 0; i < inputString.length; ++i)
    {
        switch (inputString[i])
        {
            case '&':
                output.push("&amp;");
                break;
            case '"':
                output.push("&quot;");
                break;
            case "<":
                output.push("&lt;");
                break;
            case ">":
                output.push("&gt;");
                break;
            default:
                output.push(inputString[i]);
        }


    }

    return output.join("");
}

0

Технічно &, <та> не є дійсними символами імен сутності XML. Якщо ви не можете довіряти ключовій змінній, вам слід відфільтрувати їх.

Якщо ви хочете, щоб вони були захищені як HTML-сутності, ви можете використати щось на зразок http://www.strictly-software.com/htmlencode .


0

якщо щось вдалося уникнути раніше, ви можете спробувати це, оскільки це не подвоїться, як багато інших

function escape(text) {
    return String(text).replace(/(['"<>&'])(\w+;)?/g, (match, char, escaped) => {
        if(escaped) 
            return match

        switch(char) {
            case '\'': return '&quot;'
            case '"': return '&apos;'
            case '<': return '&lt;'
            case '>': return '&gt;'
            case '&': return '&amp;'
        }
    })
}

-1

Це просто:

sText = ("" + sText).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;");

1
в якому світі це "просте" порівняно з вищезазначеними методами заміни?
Сем Холдер

Просто писати, я не говорив, що це простіше порівняно з тими, що вказані вище. Це просто інакше
Per Ghosh

4
Я заплутався, намагаючись придумати гірше рішення
розробник

@developerbmw, якщо ви не хочете додавати метод і не використовувати jquery, це одне з найкращих рішень
Per Ghosh

1
@developerbmw для читабельності, іноді краще писати код, тобто способом, що дозволяє читати функціонал зверху вниз. Динамічні мови дуже легко зробити нечитабельними швидко. Це залежить від ситуації. Також використовуються компоненти, які використовуються в різних сценаріях і потребують власної логіки. Невеликий компонент може не потребувати функцій, якщо логіка використовується лише в одному методі. Я розробник C ++ і кодую багато C. C дуже легко читати, і якщо ви знаєте, чому, тоді ви знаєте мій аргумент з цього приводу
Per Ghosh,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.