jQuery.parseJSON, JSON'daki tek tırnak işareti nedeniyle "Geçersiz JSON" hatası veriyor


202

Kullanarak sunucuma istekte bulunuyorum jQuery.post()ve sunucum JSON nesneleri (gibi { "var": "value", ... }) döndürüyor . Ancak, değerlerden herhangi biri tek bir alıntı içeriyorsa (düzgün şekilde kaçarsa \'), jQuery başka bir şekilde geçerli bir JSON dizesini ayrıştırmada başarısız olur. İşte ne demek istediğime bir örnek ( Chrome konsolunda yapılır ):

data = "{ \"status\": \"success\", \"newHtml\": \"Hello \\\'x\" }";
eval("x = " + data); // { newHtml: "Hello 'x", status: "success" }

$.parseJSON(data); // Invalid JSON: { "status": "success", "newHtml": "Hello \'x" }

Bu normal mi? Tek bir teklifi JSON üzerinden düzgün bir şekilde iletmenin bir yolu yok mu?

Yanıtlar:


325

JSON web sitesindeki durum makine şemasına göre , tek tırnak değil, yalnızca kaçan çift tırnak karakterlerine izin verilir. Tek tırnak işareti karakterlerinin kaçmasına gerek yoktur:

http://www.json.org/string.gif


Güncelleme - İlgilenenler için daha fazla bilgi:


Douglas Crockford, JSON belirtiminin neden dizeler içinde kaçan tek tırnaklara izin vermediğini özellikle söylemez. Bununla birlikte, JavaScript: İyi Parçalar'ın Ek E'deki JSON tartışması sırasında şunları yazıyor:

JSON'un tasarım hedefleri minimal, taşınabilir, metinsel ve JavaScript'in bir alt kümesi olacaktı. Birlikte çalışmak için ne kadar az anlaşmaya varırsak, o kadar kolay birlikte çalışabiliriz.

Bu yüzden, belki de tüm JSON uygulamalarının üzerinde anlaşması gereken daha az bir kural olduğundan, sadece çift tırnak kullanarak dizelerin tanımlanmasına izin vermeye karar verdi. Sonuç olarak, bir dizedeki tek bir tırnak karakterinin dizeyi yanlışlıkla sonlandırması imkansızdır, çünkü tanım gereği bir dize yalnızca bir çift tırnak karakteriyle sonlandırılabilir. Bu nedenle, resmi spesifikasyonda tek bir tırnak karakterinden kaçmaya izin verilmesine gerek yoktur.


Biraz daha derin kazma, Crockford en org.json Java için JSON uygulanması daha caizdir ve does tek tırnak karakterleri izin:

ToString yöntemleriyle üretilen metinler JSON sözdizimi kurallarına kesinlikle uygundur. Yapıcılar kabul edecekleri metinlerde daha bağışlayıcıdır:

...

  • Dizeler '(tek tırnak) ile tırnak içine alınabilir.

Bu, JSONTokener kaynak kodu tarafından onaylanır . nextStringYöntem tek tırnak karakterleri ve sadece çift tırnak karakterleri gibi davranır onları kaçtı kabul eder:

public String nextString(char quote) throws JSONException {
    char c;
    StringBuffer sb = new StringBuffer();
    for (;;) {
        c = next();
        switch (c) {

        ...

        case '\\':
            c = this.next();
            switch (c) {

            ...

            case '"':
            case '\'':
            case '\\':
            case '/':
                sb.append(c);
                break;
        ...

Yöntemin üstünde bilgilendirici bir yorum vardır:

Resmi JSON biçimi, tek tırnak içinde dizelere izin vermez, ancak bir uygulamanın bunları kabul etmesine izin verilir.

Yani bazı uygulamalar tek tırnakları kabul edecektir - ancak buna güvenmemelisiniz. Birçok popüler uygulama bu bağlamda oldukça kısıtlayıcıdır ve tek tırnaklı dizeler ve / veya kaçan tek tırnaklar içeren JSON'u reddedecektir.


Son olarak, bunu tekrar orijinal soruya bağlamak için, jQuery.parseJSONilk olarak tarayıcının yerel JSON ayrıştırıcısını veya json2.js gibi yüklü bir kütüphaneyi kullanmaya çalışır (eğer bir yan notta jQuery mantığının JSONtanımlanmamışsa dayandığı kütüphanedir ) . Bu nedenle jQuery, yalnızca temeldeki uygulama kadar izin verilebilir:

parseJSON: function( data ) {
    ...

    // Attempt to parse using the native JSON parser first
    if ( window.JSON && window.JSON.parse ) {
        return window.JSON.parse( data );
    }

    ...

    jQuery.error( "Invalid JSON: " + data );
},

Bildiğim kadarıyla bu uygulamalar sadece resmi JSON şartnamesine uyuyor ve tek tırnak kabul etmiyor, dolayısıyla jQuery de yok.


4
UPDATE :: JQuery, JSON yapıştırılırken çok kısıtlayıcıdır. Uyarıyı denerseniz ($. ParseJSON ("[\" Ciao \\ '\ "]")); Justin'in bildirdiği için çalışmıyor
daitangio

2
" Crockford'un JSON for Java uygulamasının org.json uygulamasına izin veriliyor ve tek tırnak karakterlerine izin veriyor " # Bu sadece iyi bir uygulama: sağlamlık ilkesi
Duncan Jones

1
@DuncanJones - Bu makale, tarayıcıların hiçbirinin JSON ile ilgili olarak bu ilkeyi neden takip etmediği konusunda fikir verebilir
Justin Ethier

1
@JustinEthier , JSON spec tools.ietf.org/html/rfc7159 bu cevaba göre stackoverflow.com/a/25491642/759452 belirttiği gibi , bazı uygulamaların neden tek tırnakların kaçmasına izin vereceğini açıklayabilir. Any character may be escaped
Adrien Be

1
@AdrienBe - İlginç ... ama 4 onaltılık basamaktan oluşuyorsa herhangi bir karakterin kaçabileceği anlamına mı geliyorlardı? Hem yukarıdaki durum şemasına hem de RFC'nin 7. bölümünde yer alan şemaya göre, yazılı olarak tek bir alıntıdan kaçmaya \'hala izin verilmemektedir. RFC bu noktada daha açık olsaydı iyi olurdu.
Justin Ethier

15

Bir dize içinde tek bir alıntı gerekiyorsa, \ 'spesifikasyon tarafından tanımlanmadığından, hepsi için \u0027 bkz. Http://www.utf8-chartable.de/

edit: lütfen yorumlarda kelime backticks yanlış kullanımımı affedersiniz. Ters eğiklik demek istedim. Demek istediğim, dizeleri diğer dizelerin içine yerleştirdiğinizde, tek bir alıntıdan kaçmak için çok sayıda ters eğik çizgi yerine unicode kullanmanın daha yararlı ve okunabilir olabileceğini düşünüyorum. Eğer iç içe değilseniz, ancak orada sadece düz eski bir teklif koymak gerçekten daha kolaydır.


29
Hayır. Basit bir tek tırnak kullanın.
Jeff Kaufman

Bazen, unicode kullanmak tonlarca arka kenara göre daha kolaydır. Özellikle dönüşümlü keneler içinde.
slf

3
Neden geri tepmelere ihtiyacınız var? "Foo 'bar'" gibi bir dizeniz varsa, tek tırnak işaretlerinden kaçınmalısınız.
Jeff Kaufman

3
tam olarak aradığım şey. Bir js string var gibi bir sayfaya bir json dizesi yazmaya çalışıyorum ve tek tırnak içine alın ve bir özellik değeri ne zaman tek bir teklif vardı erken sona erdi. Şimdi sadece bir json.Replace ("'", "\ u0027") kodunu sayfaya yazmadan önce yapıyorum.
Zack

@Zack JSON'u tırnak işaretleri içine almamalısınız. Mevcut bir JSON dizesinden bir dizeye ihtiyacınız varsa, dizgiyi yeniden dizeleyin. PHP'de, bu bir önceki sonucun var jsonEncodedAsString = <?= json_encode(myEncodedJson) ?>nerede myEncodedJsonolduğu olurdu json_encodeBu tek alıntı kaçan ilgilenir, aslında, sadece çift tırnak içine sarılmış büyük bir dize çıktı, böylece tek tırnak kaçmaz, ama çift tırnak niyet.
Juan Mendes

5

Sorunun nerede olduğunu ve spesifikasyonlara baktığımda, kaçan tek tırnakların doğru bir şekilde ayrıştırılması gerektiği anlaşılıyor.

JSON dizesini ayrıştırmak için jquery`s jQuery.parseJSON işlevini kullanıyorum ama json_encode ile hazırlanan verilerde tek bir teklif olduğunda hala ayrıştırma hatası alıyorum.

Benim uygulamada böyle bir hata olabilir (PHP - sunucu tarafı):

$data = array();

$elem = array();
$elem['name'] = 'Erik';
$elem['position'] = 'PHP Programmer';
$data[] = json_encode($elem);

$elem = array();
$elem['name'] = 'Carl';
$elem['position'] = 'C Programmer';
$data[] = json_encode($elem);

$jsonString = "[" . implode(", ", $data) . "]";

Son adım JSON kodlu dizesini bir JS değişkenine depolamaktır:

<script type="text/javascript">
employees = jQuery.parseJSON('<?=$marker; ?>');
</script>

'' Yerine "" kullanırsam yine de hata verir.

ÇÖZÜM:

Benim için işe yarayan tek şey, bitmask JSON_HEX_APOS'u bu tür tek tırnakları dönüştürmek için kullanmaktı:

json_encode($tmp, JSON_HEX_APOS);

Bu sorunu çözmenin başka bir yolu var mı? Kodum yanlış mı yoksa kötü yazılmış mı?

Teşekkürler


'<= $ işaretleyici; ?> 'bu geçerli değil json. Çevredeki alıntılar javascript yorumlayıcısı tarafından "yeniliyor" ve <... ile gerçekten başlamak istediğiniz bir dize bırakarak bunlardan biriydi: jQuery.parseJSON ('"<? = $ Marker;?>" '); jQuery.parseJSON ("\" <? = $ işaretleyici;?> \ ""); Spesifikasyona göre, json dizeleri çift tırnak kullanmalıdır, ancak javascript umurunda değildir, bu nedenle, ya tek tırnaklı bir javascript dizeniz veya çift tırnak kullanırsınız, ancak ikincisini kullanırsanız, tüm dize içinde çift tırnak kullanır.
Chris Cogdon

3

Bir sorguda tek bir teklif gönderirken

empid = " T'via"
empid =escape(empid)

Tek bir teklif içeren değeri aldığınızda

var xxx  = request.QueryString("empid")
xxx= unscape(xxx)

Sorguda tek bir alıntı içeren değeri aramak / eklemek istiyorsanız xxx=Replace(empid,"'","''")


1
Ancak bir JSON dizesinin parçası olarak geçerken tek bir alıntıdan kaçmaya gerek yoktur ...
Justin Ethier

2

PHP'nin anadilini kullanarak bir JavaScript komut dosyası bloğu çıkarmak için CakePHP'yi kullanırken benzer bir soruna dikkat çekmek json_encode. $contractorCompaniestek tırnak işareti içeren değerler içerir ve yukarıda açıklandığı ve beklendiği json_encode($contractorCompanies)gibi geçerli JSON nedeniyle bunlardan kaçmaz.

<?php $this->Html->scriptBlock("var contractorCompanies = jQuery.parseJSON( '".(json_encode($contractorCompanies)."' );"); ?>

JSON kodlu dizenin etrafına addslashes () ekleyerek, Cake / PHP'nin tarayıcıya doğru javascript'i yansıtabilmesini sağlayan tırnak işaretlerinden kaçarsınız. JS hataları ortadan kalkar.

<?php $this->Html->scriptBlock("var contractorCompanies = jQuery.parseJSON( '".addslashes(json_encode($contractorCompanies))."' );"); ?>

1

Bir HTML5 data- * özniteliğine bir XHR isteğinden bir JSON nesnesi kaydetmeye çalışıyordum. Yukarıdaki çözümlerin çoğunu başarıyla denedim.

Sonunda ne sonunda sonunda stringify () yöntemi aşağıdaki şekilde çağırdıktan sonra bir regex kullanarak tek 'kodu onunla kodu yerine &#39;:

var productToString = JSON.stringify(productObject);
var quoteReplaced = productToString.replace(/'/g, "&#39;");
var anchor = '<a data-product=\'' + quoteReplaced + '\' href=\'#\'>' + productObject.name + '</a>';
// Here you can use the "anchor" variable to update your DOM element.

0

İlginç. Sunucu ucunda JSON'unuzu nasıl üretiyorsunuz? Bir kütüphane işlevi mi kullanıyorsunuz ( json_encodePHP gibi ) veya JSON dizesini elle mi oluşturuyorsunuz?

Dikkatimi çeken tek şey kaçış kesme işareti ( \'). Çift tırnak kullandığınızı gördüğünüzde, gerçekten de gerektiği gibi, tek tırnaktan kaçmanıza gerek yoktur. Kendimi henüz 1.4.1 sürümüne güncellemediğim için, bunun gerçekten jQuery hatasının nedeni olup olmadığını kontrol edemiyorum.


Ben JSON nesne oluşturmak için PHP bir kütüphane kullanın - bunu yazmak için özledim. Bunu işaret ettiğiniz için teşekkürler.
Erik Čerpnjak
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.