Kod çözme & amp; JavaScript'e ve JavaScript'e dön


230

Benim gibi dizelerim var

var str = 'One & two & three';

web sunucusu tarafından HTML'ye dönüştürülür. Bu dizeleri dönüştürmem gerek

'One & two & three'

Şu anda, (jQuery yardımıyla) bunu yapıyorum:

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

Ancak yanlış yaptığımı huzursuz eden bir his var. denedim

unescape("&")

ancak işe yaramaz gibi görünüyor, decodeURI / decodeURIComponent de çalışmıyor.

Bunu yapmanın başka, daha doğal ve zarif yolları var mı?


Bu makalede bulunan büyük işlev iyi çalışıyor gibi görünüyor: blogs.msdn.com/b/aoakley/archive/2003/11/12/49645.aspx Bunun en akıllıca çözüm olduğunu düşünmüyorum ama işe yarıyor.
Matias

1
HTML objeleri içeren dizeler escaped veya URI kodlu dizelerden farklı bir şey olduğundan , bu işlevler çalışmaz.
Marcel Korpel

1
@Matias, söz konusu işlev 2003'te yazıldığı için HTML'ye yeni adlandırılmış varlıklar eklendiğini (örneğin, HTML 5 spesifikasyonu aracılığıyla) not eder - örneğin, tanımaz 𝕫. Bu, gelişen bir spesifikasyonla ilgili bir sorundur; bu nedenle, aslında çözmek için tutulan bir araç seçmelisiniz.
Mark Amery

1
@MarkAmery evet, tamamen katılıyorum! Birkaç yıl sonra bu sorulara geri dönmek güzel bir deneyim, teşekkürler!
Matias

Yanıtlar:


105

JavaScript'ten HTML'yi (metin vb.) Yorumlamak için daha modern bir seçenek, API'deki HTML desteğidir DOMParser( buraya MDN'de bakın ). Bu, bir dizeyi HTML belgesine dönüştürmek için tarayıcının yerel HTML ayrıştırıcısını kullanmanızı sağlar. 2014'ün sonlarından beri tüm büyük tarayıcıların yeni sürümlerinde desteklenmektedir.

Sadece bazı metin içeriğinin kodunu çözmek istiyorsak, bunu bir belge gövdesindeki tek içerik olarak koyabilir, belgeyi ayrıştırabilir ve içeriğini çıkarabiliriz .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);

Biz de görebileceğiniz üzere taslak şartnameDOMParser JavaScript çözümlü belge için etkin olmadığını biz güvenlik kaygıları olmadan bu metin dönüştürme işlemini, böylece.

parseFromString(str, type)Yöntem bağlı olarak aşağıdaki adımları çalıştırmalısınız türü :

  • "text/html"

    Ayrıştırma str bir ile HTML parser, ve yeni oluşturulan dönmek Document.

    Komut dosyası bayrağı "devre dışı" olarak ayarlanmalıdır.

    NOT

    scriptöğeler gerçekleştirilemez noscriptolarak işaretlenir ve içeriği biçimlendirme olarak ayrıştırılır.

Bu sorunun kapsamı dışındadır, ama not memnun sen çözümlü DOM düğümleri kendileri (değil sadece kendi metin içeriği) alarak ve canlı belge DOM taşıyarak eğer, bu onların betik reenabled olacağını mümkün olduğunu ve orada olabilir güvenlik kaygısı olmak. Araştırmadım, lütfen dikkatli olun.


6
NodeJ'ler için alternatif var mı?
coderInrRain


285

Tüm kodlanmış HTML varlıklarının kodunu çözmeniz mi gerekiyor yoksa &amp; kendisinin ?

Sadece idare etmeniz gerekiyorsa, &amp;bunu yapabilirsiniz:

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

Tüm HTML varlıklarının kodunu çözmeniz gerekiyorsa, bunu jQuery olmadan yapabilirsiniz:

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

Lütfen Mark'ın bu cevabın önceki bir sürümünde güvenlik açıklarını vurgulayan ve potansiyel XSS güvenlik açıklarına karşı hafifletmek textareayerine kullanılmasını öneren yorumlarına dikkat edin div. Bu güvenlik açıkları, jQuery veya düz JavaScript kullansanız da vardır.


16
Dikkat! Bu potansiyel olarak güvensizdir. Eğer encoded='<img src="bla" onerror="alert(1)">'öyleyse yukarıdaki pasaj bir uyarı gösterecektir. Bu, kodlanmış metniniz kullanıcı girişinden geliyorsa, bu snippet ile deşifre edilmesi bir XSS güvenlik açığı oluşturabilir.
Mark Amery

@MarkAmery Ben bir güvenlik uzmanı değilim, ancak nullmetni aldıktan sonra div'i hemen ayarladıysanız, img'deki
Mottie

4
@Mottie, sizin için hangi tarayıcıda çalıştığından emin olun, ancak alert(1)yine de OS X'de Chrome'da ateş ediyor. Bu kesmek için güvenli bir varyant istiyorsanız, a'yı kullanmayıtextarea deneyin .
Mark Amery

Sadece bir tür html varlığı için basit regexp yerine alternatif için +1. Eğer bir python şişesi uygulamasından bir şablona enterpolasyon html veri bekliyorsanız bunu kullanın.
OzzyTheGiant

1
Düğüm sunucusunda bu nasıl yapılır?
Mohammad Kermani

45

Matthias Bynens'in bunun için bir kütüphanesi var: https://github.com/mathiasbynens/he

Misal:

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

Bir öğenin HTML içeriğini ayarlama ve daha sonra metin içeriğini okuma içeren kesmek üzerinde tercih öneririz. Bu tür yaklaşımlar işe yarayabilir, ancak güvenilmez kullanıcı girişlerinde kullanıldığında aldatıcı derecede tehlikeli ve XSS fırsatları sunar.

Bir kitaplığa gerçekten yüklemeye dayanamıyorsanız, önerilen çeşitli benzer yaklaşımların aksine, bildiğim hiçbir güvenlik boşluğu olmayan, neredeyse yinelenen bir sorunun cevabındatextarea açıklanan hack'i kullanabilirsiniz :

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

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

Ama buna benzer yaklaşımları etkileyen güvenlik konularını, bağlantılı cevapta listelediğime dikkat edin! Bu yaklaşım bir hack'tir ve a'nın textarea(veya belirli tarayıcılarda hataların) izin verilen içeriğinde gelecekte yapılacak değişiklikler, aniden bir gün bir XSS deliğine sahip olan koda neden olabilir.


1
Matthias Bynens'in kütüphanesi hekesinlikle harika! Tavsiye için çok teşekkür ederim!
Pedro A

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

Bu ExtJS kaynak kodundan.


4
1; bu, isimlendirilmiş varlıkların büyük çoğunluğunu ele almaz. Örneğin, htmlEnDecode.htmlDecode('&euro;')geri dönmeli '€', bunun yerine geri dönmelidir '&euro;'.
Mark Amery


15

Lodash unescape / escape işlevini kullanabilirsiniz https://lodash.com/docs/4.17.5#unescape

import unescape from 'lodash/unescape';

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

str olacak 'fred, barney, & pebbles'


1
muhtemelen 'lodash / unescape'ten' _unescape'i içe aktarma '; bu nedenle aynı adın kullanımdan kaldırılmış javascript işleviyle
çakışmıyor

14

Aradığınızda, benim gibi - bu arada güzel ve güvenli bir JQuery yöntemi var.

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

Sen f.ex. bunu konsolunuza yazın:

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

Bu nedenle $ .parseHTML (x) bir dizi döndürür ve metninizde HTML işaretlemesi varsa, array.length değeri 1'den büyük olur.


Benim için mükemmel çalıştı, tam da aradığım şey buydu, teşekkürler.
Jonathan Nielsen

1
Yukarıdakilerin xdeğeri varsa <script>alert('hello');</script>çökecektir. Geçerli jQuery ise aslında komut dosyasını çalıştırın denemez ama [0]verecektir undefinedçağrı kadar textContentbaşarısız olur ve komut orada duracaktır. $('<div />').html(x).text();daha güvenli görünüyor - gist.github.com/jmblog/3222899
Andrew Hodgkinson

@AndrewHodgkinson evet, ancak soru "Kod çözme ve JavaScript'e geri dönme" idi - bu nedenle önce x'in içeriğini test eder veya yalnızca doğru durumlarda kullandığınızdan emin olursunuz.
cslotty

Bunun nasıl olduğunu gerçekten göremiyorum. Yukarıdaki kod her durumda çalışır. Ve tam olarak x değerinin sabitlenmesi gereken "emin" misiniz? Ve yukarıdaki komut dosyası örneği '& amp;' gerçekten düzeltmeye ihtiyacı vardı? OP dizelerinin nereden geldiği hakkında hiçbir fikrimiz yok, bu yüzden kötü niyetli girdiler dikkate alınmalıdır.
Andrew Hodgkinson

@AndrewHodgkinson Düşüncelerinizi beğendim, ancak buradaki soru bu değil. Yine de bu soruya cevap vermekten çekinmeyin. Sanırım komut dosyası etiketlerini kaldırabilirsiniz, f.ex.
cslotty

8

jQuery sizin için kodlayacak ve kodunu çözecektir. Ancak, div değil, textarea etiketi kullanmanız gerekir.

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, çünkü bazıları hala önemli bir kullanıcı tabanına sahip olan eski jQuery sürümleri için (şaşırtıcı) bir güvenlik açığı olduğundan, bu sürümler geçirilen HTML'deki komut dosyalarını algılar ve açıkça değerlendirir.html() . Bu nedenle burada bile textareagüvenliği sağlamak için a kullanmak yeterli değildir; Bu görev için jQuery kullanarak ve düz DOM API ile eşdeğer kod yazma öneririz . (Evet, jQuery'nin bu eski davranışı çılgın ve korkunç.)
Mark Amery

Bunu işaret ettiğiniz için teşekkürler. Bununla birlikte, soru script enjeksiyonunu kontrol etme zorunluluğunu içermez. Soru özellikle web sunucusu tarafından oluşturulan html hakkında sorular soruyor. Bir web sunucusuna kaydedilen html içeriği muhtemelen kaydetmeden önce komut dosyası enjeksiyonu için doğrulanmalıdır.
Jason Williams

4

Önce <span id="decodeIt" style="display:none;"></span>vücutta bir yer yarat

Ardından, innerHTML olarak kodu çözülecek dizeyi buna atayın:

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

En sonunda,

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

İşte genel kod:

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

1
1; bu güvenilmeyen girdide kullanmak tehlikeli değildir. Örneğin, stringtodecodebenzer bir şey içeriyorsa ne olacağını düşünün <script>alert(1)</script>.
Mark Amery

2

yaygın olanları yakalayan bir javascript çözümü:

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

bu https://stackoverflow.com/a/4835406/2738039 adresinin tersidir.


map[c] || ''Tanınmayanları kullanırsanızundefined
Eldelshell

Çok sınırlı kapsam; -1.
Mark Amery

2
+1, daha fazlası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 yeni 2015

Manuel kapsam. Tavsiye edilmez.
Sergio A.

2

Tek hatlı çocuklar için:

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

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

2

Soru, kökenini belirtmez, xancak kötü niyetli (veya sadece kendi uygulamamızdan beklenmeyen) girdilere karşı savunmak mantıklıdır. Örneğin, diyelim ki xbir değeri var &amp; <script>alert('hello');</script>. Bunu jQuery'de kullanmanın güvenli ve basit bir yolu:

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

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

Https://gist.github.com/jmblog/3222899 aracılığıyla bulundu . En azından bazı alternatiflerden daha kısa olmasa ve XSS'ye karşı savunma sağlarsa, bu çözümü kullanmaktan kaçınmak için pek çok neden göremiyorum .

(Başlangıçta bu bir yorum olarak yayınlanmıştır, ancak aynı iş parçacığı bir sonraki yorum ben bunu istedi beri bir cevap olarak ekliyorum).


1

Bir JSON dizisinden kaldırmak için her şeyi denedim. Yukarıdaki örneklerin hiçbiri, ancak https://stackoverflow.com/users/2030321/chris , sorunumu düzeltmeme neden olan harika bir çözüm verdi.

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

Ben bir dizi JSON veri çekiyordu kalıcı bir pencereye eklemek için nasıl anlamadı, çünkü ben kullanmadım, ama ben örnek dayalı bu denemek ve çalıştı:

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

Sevdim çünkü basitti ve işe yarıyor, ama neden yaygın olarak kullanılmadığından emin değilim. Basit bir çözüm bulmak için yüksek ve düşük arandı. Sözdizimini ve bunu kullanma riski olup olmadığını anlamak için çalışmaya devam ediyorum. Henüz bir şey bulamadım.


İlk teklifiniz biraz zor, ama fazla çaba harcamadan güzel çalışıyor. İkincisi ise karakterlerin kodunu çözmek için sadece kaba kuvvet kullanır; Bu, tam bir kod çözme işlevini gerçekleştirmek için çok çaba ve zaman harcayabileceği anlamına gelir. Bu yüzden hiç kimse OP'nin problemini çözmek için bu yolu kullanmıyor.
Sergio A.

-1

function decodeHTMLContent(htmlText) {
  var txt = document.createElement("span");
  txt.innerHTML = htmlText;
  return txt.innerText;
}

var result = decodeHTMLContent('One &amp; two &amp; three');
console.log(result);


Bu cevap yıllar önce textareaverilen cevaptan nasıl daha iyi ?
Dan Dascalescu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.