jQuery.parseJSON โยนข้อผิดพลาด“ JSON ไม่ถูกต้อง” เนื่องจากหนีคำพูดเดียวใน JSON


202

ฉันกำลังส่งคำขอไปยังเซิร์ฟเวอร์โดยใช้jQuery.post()เซิร์ฟเวอร์ของฉันกำลังส่งคืนวัตถุ JSON (เช่น{ "var": "value", ... }) อย่างไรก็ตามหากค่าใด ๆ มีเครื่องหมายคำพูดเดี่ยว (หนีออกมาอย่างเหมาะสม\') jQuery ไม่สามารถแยกสตริง JSON ที่ถูกต้องเป็นอย่างอื่น นี่คือตัวอย่างของสิ่งที่ฉันหมายถึง ( ทำในคอนโซลของ Chrome ):

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

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

เป็นเรื่องปกติหรือไม่ ไม่มีวิธีที่จะผ่านการเสนอราคาเดียวผ่าน JSON อย่างถูกต้องหรือไม่?

คำตอบ:


325

ตามแผนภาพไดอะแกรมของเครื่องจักรบนเว็บไซต์ JSONอนุญาตให้ใช้อักขระเครื่องหมายคำพูดคู่ที่ยกเว้นเท่านั้นไม่ใช่เครื่องหมายคำพูดเดี่ยว ไม่จำเป็นต้องใช้อักขระเครื่องหมายคำพูดเดี่ยว:

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


อัปเดต - ข้อมูลเพิ่มเติมสำหรับผู้ที่สนใจ:


ดักลาสคร็อคฟอร์ดไม่ได้ระบุว่าทำไมคุณสมบัติของ JSON จึงไม่อนุญาตให้ใช้เครื่องหมายคำพูดเดี่ยวในสตริง อย่างไรก็ตามในระหว่างการสนทนาของ JSON ในภาคผนวก E ของ JavaScript: The Good Partsเขาเขียน:

เป้าหมายการออกแบบของ JSON นั้นต้องมีความเรียบง่ายพกพาสะดวกข้อความและชุดย่อยของ JavaScript ยิ่งเราต้องตกลงให้น้อยลงเพื่อให้สามารถทำงานร่วมกันได้มากขึ้นเราก็สามารถทำงานร่วมกันได้ง่ายขึ้น

ดังนั้นเขาอาจตัดสินใจอนุญาตเฉพาะสตริงที่จะถูกกำหนดโดยใช้เครื่องหมายคำพูดคู่เนื่องจากนี่เป็นกฎที่น้อยกว่าที่การอิมพลีเมนต์ JSON ทั้งหมดต้องเห็นด้วย ดังนั้นจึงเป็นไปไม่ได้ที่อักขระอัญประกาศเดี่ยวภายในสตริงจะยกเลิกสตริงโดยไม่ตั้งใจเนื่องจากตามคำจำกัดความสตริงสามารถยกเลิกได้ด้วยอักขระเครื่องหมายคำพูดคู่เท่านั้น ดังนั้นจึงไม่จำเป็นต้องอนุญาตให้มีการหลบหนีของอักขระคำพูดเดียวในข้อกำหนดอย่างเป็นทางการ


ขุดนิด ๆ หน่อย ๆ ลึก Crockford ของorg.jsonการดำเนินงานของ JSON สำหรับ Java ได้รับอนุญาตมากขึ้นและไม่อนุญาตให้มีตัวอักษรคำพูดเดียว:

ข้อความที่ผลิตโดยวิธี toString อย่างเคร่งครัดสอดคล้องกับกฎไวยากรณ์ JSON คอนสตรัคชันให้อภัยมากกว่าในข้อความที่พวกเขาจะยอมรับ:

...

  • สายอักขระอาจจะยกมาด้วย '(คำพูดเดียว)

สิ่งนี้ได้รับการยืนยันโดยซอร์สโค้ดJSONTokener nextStringวิธีการยอมรับอักขระอัญประกาศเดี่ยวและใช้กับอักขระเครื่องหมายคำพูดคู่:

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;
        ...

ที่ด้านบนของวิธีการมีความคิดเห็นให้ข้อมูล:

รูปแบบ JSON ที่เป็นทางการไม่อนุญาตให้ใช้สตริงในเครื่องหมายคำพูดเดี่ยว แต่การใช้งานจะได้รับอนุญาตให้ยอมรับได้

ดังนั้นการใช้งานบางอย่างจะยอมรับคำพูดเดียว - แต่คุณไม่ควรพึ่งพาสิ่งนี้ การใช้งานที่ได้รับความนิยมจำนวนมากค่อนข้าง จำกัด ในเรื่องนี้และจะปฏิเสธ JSON ที่มีสตริงที่ยกมาเดี่ยวและ / หรือคำพูดเดียวที่หลบหนี


สุดท้ายที่จะผูกกลับไปที่คำถามเดิมjQuery.parseJSONพยายามครั้งแรกที่จะใช้ตัวแยกวิเคราะห์ JSON ดั้งเดิมของเบราว์เซอร์หรือห้องสมุดที่โหลดเช่นjson2.jsที่ใช้งานได้ (ซึ่งในบันทึกด้านข้างเป็นห้องสมุดตรรกะ jQuery จะขึ้นอยู่กับถ้าJSONไม่ได้กำหนดไว้) . ดังนั้น jQuery สามารถอนุญาตได้เช่นเดียวกับการติดตั้งพื้นฐาน:

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

เท่าที่ฉันรู้ว่าการใช้งานเหล่านี้เป็นไปตามข้อกำหนดของ JSON อย่างเป็นทางการเท่านั้นและไม่ยอมรับเครื่องหมายคำพูดเดี่ยวดังนั้น jQuery


4
ปรับปรุง :: JQuery มีข้อ จำกัด มากเมื่อ pasing JSON หากคุณลองแจ้งเตือน ($. parseJSON ("[\" Ciao \\ '\ "]"))) มันไม่ทำงานเพราะสิ่งที่จัสตินรายงาน
daitangio

2
" การใช้งาน org.json ของ Crockford สำหรับ JSON สำหรับ Java นั้นอนุญาตได้มากขึ้นและอนุญาตให้ใช้อักขระเครื่องหมายคำพูดเดี่ยว " # นั่นเป็นวิธีปฏิบัติที่ดีเพียงอย่างเดียว: หลักการความทนทาน
Duncan Jones

1
@DuncanJones - บทความนี้อาจให้ข้อมูลเชิงลึกว่าทำไมไม่มีเบราว์เซอร์ใดที่ดูเหมือนจะปฏิบัติตามหลักการดังกล่าวเกี่ยวกับ JSON: joelonsoftware.com/items/2008/03/17.html
Justin Ethier

1
@Justin อีกครั้งตามที่ระบุไว้โดยคำตอบนี้stackoverflow.com/a/25491642/759452เครื่องมือข้อมูลจำเพาะJSON.ietf.org/html/rfc7159กล่าวว่าAny character may be escapedสิ่งนี้อาจอธิบายได้ว่าเหตุใดการใช้งานบางอย่างจึงทำให้สามารถหลีกเลี่ยงการเสนอราคาเดี่ยวได้
Adrien เป็น

1
@AdrienBe - น่าสนใจ ... แต่พวกเขาหมายถึงตัวละครใด ๆ ที่อาจจะหลบหนีถ้ามันประกอบด้วยตัวเลขฐานสิบหก 4? จากทั้งแผนภาพของรัฐข้างต้นและหนึ่งในส่วนที่ 7 ของ RFC การหลบหนีของคำพูดเดียวที่เขียน\'ยังไม่ได้รับอนุญาต มันจะดีถ้า RFC มีความชัดเจนมากขึ้นในประเด็นนี้
Justin Ethier

15

หากคุณต้องการใบเสนอราคาเดียวภายในสตริงเนื่องจาก \ 'ไม่ได้กำหนดโดย spec ให้ใช้\u0027 ดูที่http://www.utf8-chartable.de/สำหรับพวกเขาทั้งหมด

แก้ไข: โปรดแก้ตัวการนำ backticks ของฉันไปใช้ผิด ๆ ในความคิดเห็น ฉันหมายถึงแบ็กสแลช จุดของฉันที่นี่คือในกรณีที่คุณมีสตริงที่ซ้อนกันภายในสตริงอื่น ๆ ฉันคิดว่ามันจะมีประโยชน์มากขึ้นและสามารถอ่านได้โดยใช้ยูนิโค้ดแทนแบ็กสแลชจำนวนมากเพื่อหลีกเลี่ยงคำพูดเดียว หากคุณยังไม่ได้ซ้อนกันมันง่ายกว่าที่จะแค่ใส่ข้อความเก่า ๆ ไว้ในนั้น


29
ไม่เพียงใช้ใบเสนอราคาเดียวแบบธรรมดา
Jeff Kaufman

บางครั้งมันง่ายกว่าการใช้ unicode มากกว่า back-ticks มากมาย โดยเฉพาะอย่างยิ่งเมื่อมีการสลับข้างหลังเห็บ
slf

3
ทำไมคุณต้องการ backticks หากคุณมีสตริงเช่น "foo 'bar'" คุณเพียง แต่ไม่ใส่เครื่องหมายคำพูดเดี่ยว
Jeff Kaufman

3
สิ่งที่ฉันกำลังมองหา ฉันพยายามเขียนสตริง json ลงบนหน้าเว็บเป็นสตริง js และใส่ไว้ในเครื่องหมายคำพูดเดี่ยวและมันจะยุติลงก่อนเมื่อใดก็ตามที่ค่าคุณสมบัติมีเครื่องหมายคำพูดเดี่ยวอยู่ ตอนนี้ฉันเพิ่งทำ json. แทนที่ ("'", "\ u0027") ในโค้ดด้านหลังก่อนเขียนลงบนหน้า
Zack

@Zack คุณไม่ควรใส่ JSON ด้วยเครื่องหมายคำพูด หากคุณต้องการสตริงออกจากสตริง JSON ที่มีอยู่เพียงแค่ทำให้เป็นสตริงอีกครั้ง ใน PHP นั้นจะเป็นvar jsonEncodedAsString = <?= json_encode(myEncodedJson) ?>ที่ที่myEncodedJsonผลลัพธ์ของก่อนหน้านี้json_encodeซึ่งจะคอยหลบหนีใบเสนอราคาของคุณจริง ๆ แล้วมันจะเอาท์พุทสตริงขนาดใหญ่ที่ห่อหุ้มด้วยเครื่องหมายคำพูดคู่ดังนั้นการเสนอราคาเดียวจะไม่ถูกหลบหนี จะ.
Juan Mendes

5

ฉันเข้าใจว่าปัญหาอยู่ที่ใดและเมื่อฉันดูรายละเอียดอย่างชัดเจนว่าการแยกคำพูดเดี่ยวที่ไม่ใช้ค่า Escape ควรวิเคราะห์คำอย่างถูกต้อง

ฉันใช้ jQuery`s jQuery.parseJSON ฟังก์ชั่นในการแยกสตริง JSON แต่ยังคงได้รับข้อผิดพลาดในการแยกวิเคราะห์เมื่อมีการเสนอราคาเดียวในข้อมูลที่จัดทำขึ้นด้วย json_encode

อาจเป็นความผิดพลาดในการติดตั้งของฉันที่มีลักษณะเช่นนี้ (PHP - ฝั่งเซิร์ฟเวอร์):

$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) . "]";

ขั้นตอนสุดท้ายคือฉันเก็บสตริงที่เข้ารหัส JSON ลงในตัวแปร JS:

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

หากฉันใช้ "" แทน "" มันยังคงมีข้อผิดพลาดเกิดขึ้น

สารละลาย:

สิ่งเดียวที่ใช้ได้ผลสำหรับฉันคือการใช้ bitmask JSON_HEX_APOS เพื่อแปลงราคาเดียวเช่นนี้:

json_encode($tmp, JSON_HEX_APOS);

มีวิธีแก้ไขปัญหานี้อีกหรือไม่ รหัสของฉันผิดหรือเขียนไม่ดี?

ขอบคุณ


'<= $ เครื่องหมาย; ?> 'นี่ไม่ใช่ json ที่ถูกต้อง เครื่องหมายคำพูดล้อมรอบคือ "eaten up" โดยล่าม javascript โดยปล่อยสตริงที่ขึ้นต้นด้วย <... สิ่งที่คุณต้องการลองจริง ๆ คือ: jQuery.parseJSON ('"<? = $ marker;?>" '); jQuery.parseJSON ("\" <? = $ marker;?> \ ""); ตามข้อมูลจำเพาะสตริง json ต้องใช้เครื่องหมายคำพูดคู่ แต่ javascript ไม่สนใจดังนั้นคุณอาจมีสตริง Javascript แบบเครื่องหมายคำพูดเดี่ยวหรือเครื่องหมายคำพูดคู่ แต่ถ้าคุณใช้อันหลังคุณต้องหลบมันทั้งหมด การใช้เครื่องหมายคำพูดคู่ภายในสตริง
Chris Cogdon

3

เมื่อคุณส่งคำพูดเดียวในแบบสอบถาม

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

เมื่อคุณได้รับค่ารวมถึงคำพูดเดียว

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

หากคุณต้องการค้นหา / ใส่ค่าที่มีเครื่องหมายคำพูดเดี่ยวในแบบสอบถาม xxx=Replace(empid,"'","''")


1
แต่ไม่มีความจำเป็นที่จะหลบหนีอ้างเดียวเมื่อผ่านมันเป็นส่วนหนึ่งของสตริง JSON ...
จัสติน Ethier

2

ที่โดดเด่นเป็นปัญหาที่คล้ายกันโดยใช้ CakePHP การส่งออกสคริปต์บล็อก JavaScript โดยใช้ PHP json_encodeพื้นเมือง $contractorCompaniesมีค่าที่มีเครื่องหมายอัญประกาศเดี่ยวตามที่อธิบายไว้ด้านบนและคาดว่าjson_encode($contractorCompanies)จะไม่สามารถหลีกเลี่ยงได้เพราะ JSON ที่ถูกต้อง

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

ด้วยการเพิ่ม addlashes () รอบ ๆ สตริงที่เข้ารหัสของ JSON คุณจะสามารถหลีกเลี่ยงเครื่องหมายคำพูดเพื่อให้ Cake / PHP สามารถสะท้อนจาวาสคริปต์ที่ถูกต้องไปยังเบราว์เซอร์ได้ ข้อผิดพลาดของ JS หายไป

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

1

ฉันพยายามบันทึกวัตถุ JSON จากคำขอ XHR ลงในแอตทริบิวต์ HTML5 data- * ฉันลองวิธีการแก้ปัญหาข้างต้นมากมายโดยไม่ประสบความสำเร็จ

สิ่งสุดท้ายที่ฉันทำคือการแทนที่ใบเสนอราคาเดียว'ด้วยโค้ด&#39;โดยใช้ regex หลังจากเมธอด stringify () เรียกวิธีต่อไปนี้:

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

น่าสนใจ คุณสร้าง JSON ของคุณบนเซิร์ฟเวอร์ได้อย่างไร คุณใช้ฟังก์ชั่นห้องสมุด (เช่นjson_encodeใน PHP) หรือคุณกำลังสร้างสตริง JSON ด้วยมือ?

สิ่งเดียวที่ดึงดูดความสนใจของฉันคืออะพอสโทรฟีหลบหนี ( \') การเห็นว่าคุณกำลังใช้เครื่องหมายคำพูดคู่ตามที่ควรจะเป็นจริงไม่จำเป็นต้องหลีกเลี่ยงคำพูดเดียว ฉันไม่สามารถตรวจสอบว่าเป็นสาเหตุของข้อผิดพลาด jQuery ของคุณหรือไม่เพราะฉันยังไม่ได้อัปเดตเป็นรุ่น 1.4.1 ด้วยตัวเอง


ฉันใช้ไลบรารี่ใน PHP เพื่อสร้างออบเจ็กต์ JSON - พลาดที่จะเขียนมันลงไป ขอบคุณสำหรับการชี้ให้เห็นว่า
Erik Čerpnjak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.