ฉันควรหลีกเลี่ยงสตริงใน JSON ได้อย่างไร


154

เมื่อสร้างข้อมูล JSON ด้วยตนเองฉันจะหลีกเลี่ยงฟิลด์สตริงได้อย่างไร ฉันควรใช้บางสิ่งบางอย่างเช่น Apache คอมมอนส์แลงก์StringEscapeUtilities.escapeHtml, StringEscapeUtilities.escapeXmlหรือฉันควรใช้java.net.URLEncoder?

ปัญหาคือเมื่อฉันใช้SEU.escapeHtmlมันไม่หนีราคาและเมื่อฉันห่อสตริงทั้งหมดในคู่ของ'JSON จะไม่ถูกสร้างขึ้นจะถูกสร้างขึ้น


20
หากคุณกำลังตัดทั้งสตริงในคู่'คุณจะถึงวาระตั้งแต่เริ่มต้น: สตริง JSON สามารถล้อมรอบ"ได้เท่านั้น ดูietf.org/rfc/rfc4627.txt
Thanatos

2
+1 สำหรับStringEscapeUtilitiesร่าง มันมีประโยชน์มาก
มูฮัมหมัด Gelbana

คำตอบ:


157

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

หลบหนีตาม RFC JSON เป็นเสรีนิยมสวย: ตัวละครเดียวที่คุณจะต้องหลบหนีอยู่\, "และรหัสควบคุม (อะไรที่น้อยกว่า U + 0020)

โครงสร้างการหลบหนีนี้ใช้เฉพาะกับ JSON คุณจะต้องใช้ฟังก์ชันเฉพาะของ JSON Escapes ทั้งหมดสามารถเขียนได้เหมือนกัน\uXXXXว่าXXXXหน่วยรหัส UTF-16 สำหรับตัวละครนั้นอยู่ที่ไหน มีช็อตคัตบางอย่างเช่น\\ซึ่งใช้งานได้ดี (และผลลัพธ์จะเล็กลงและชัดเจนขึ้น)

สำหรับรายละเอียดโปรดดูRFC

esc การหลบหนีของ JSON สร้างขึ้นบน JS ดังนั้นจึงใช้\uXXXXซึ่งXXXXเป็นหน่วยรหัส UTF-16 สำหรับรหัสคะแนนนอก BMP นี่หมายถึงการเข้ารหัสคู่ตัวแทนแทนซึ่งอาจทำให้มีขนดกเล็กน้อย (หรือคุณสามารถส่งออกอักขระโดยตรงเนื่องจากการเข้ารหัสของ JSON คือข้อความ Unicode และอนุญาตให้ใช้อักขระเฉพาะเหล่านี้)


มันถูกต้องใน JSON เช่นใน JavaScript เพื่อใส่สตริงในเครื่องหมายคำพูดคู่หรือคำพูดเดี่ยว? หรือมันจะถูกต้องเท่านั้นที่จะใส่ไว้ในเครื่องหมายคำพูดคู่?
Behrang Saeedzadeh

14
เครื่องหมายคำพูดคู่เท่านั้น ( ")
Thanatos

3
@Sergei: อักขระ{[]}:?ต้องไม่ถูก Escape ด้วยแบ็กสแลชเดี่ยว ( \:ตัวอย่างเช่นไม่ถูกต้องในสตริง JSON) สิ่งเหล่านั้นทั้งหมดสามารถเลือกที่จะหนีออกมาโดยใช้\uXXXXไวยากรณ์ที่เสียหลายไบต์ ดูที่§2.5ของ RFC
Thanatos

2
ฉันไม่แน่ใจว่ามันรองรับได้มากเพียงใด แต่จากประสบการณ์ของฉันมีการเรียกให้JSON.stringify()ทำงาน
LS

2
@BitTickler อักขระ unicode ไม่ได้คลุมเครือเลย - หมายความว่ามีจุดโค้ด (หรือแต้ม) ในสเปคของ Unicode เมื่อคุณใช้ std :: string มันเป็นพวงของอักขระ Unicode เมื่อคุณต้องการทำให้เป็นอนุกรมให้พูดกับไฟล์หรือผ่านเครือข่ายนั่นคือสิ่งที่ 'การเข้ารหัส' เข้ามาดูเหมือนว่า Thanatos ที่พวกเขาต้องการให้คุณใช้ UTF แต่ในทางเทคนิคแล้วการเข้ารหัสใด ๆ สามารถนำมาใช้ได้ตราบใดที่ มันสามารถสร้างใหม่เป็นอักขระ Unicode
เจอราร์ด ONeill

54

สารสกัดจากJettison :

 public static String quote(String string) {
         if (string == null || string.length() == 0) {
             return "\"\"";
         }

         char         c = 0;
         int          i;
         int          len = string.length();
         StringBuilder sb = new StringBuilder(len + 4);
         String       t;

         sb.append('"');
         for (i = 0; i < len; i += 1) {
             c = string.charAt(i);
             switch (c) {
             case '\\':
             case '"':
                 sb.append('\\');
                 sb.append(c);
                 break;
             case '/':
 //                if (b == '<') {
                     sb.append('\\');
 //                }
                 sb.append(c);
                 break;
             case '\b':
                 sb.append("\\b");
                 break;
             case '\t':
                 sb.append("\\t");
                 break;
             case '\n':
                 sb.append("\\n");
                 break;
             case '\f':
                 sb.append("\\f");
                 break;
             case '\r':
                sb.append("\\r");
                break;
             default:
                 if (c < ' ') {
                     t = "000" + Integer.toHexString(c);
                     sb.append("\\u" + t.substring(t.length() - 4));
                 } else {
                     sb.append(c);
                 }
             }
         }
         sb.append('"');
         return sb.toString();
     }

10
นี่คือแท็ก OP
MonoThreaded

ไม่เข้าใจเฉพาะเมื่อ c <'' เปลี่ยนเป็น \ u ในกรณีของฉันมีตัวละคร \ uD38D ซึ่งเป็น 55,357 และมากกว่า '' จึงไม่ได้เปลี่ยนไป \ U ...
Stony

1
@Stony ฟังดูเหมือนคำถามใหม่
MonoThreaded

@MonoThreaded ขอบคุณสำหรับคำตอบของคุณฉันยังไม่รู้ว่าทำไม แต่ในที่สุดฉันเปลี่ยนวิธีการแก้ไขเช่นด้านล่างถ้า (c <'' || c> 0x7f) {t = "000" + Integer.toHexString (c) .toUpperCase (); sb.append ("\\ u" + t.substring (t.length () - 4)); } else {sb.append (c); }}
Stony

1
@Stony ทุกตัวละครอื่น ๆ กว่า", \ และตัวควบคุม (เหล่านั้นก่อน“”) ที่ถูกต้องภายในสตริง JSON เป็นแมตช์ที่ตราบเท่าที่การเข้ารหัสเอาท์พุท กล่าวอีกนัยหนึ่งคุณไม่จำเป็นต้องเข้ารหัส“ 펍” \uD38Dตราบใดที่การเข้ารหัส UTF ยังคงอยู่
meustrus

37

org.codehaus.jettison.json.JSONObject.quote("your string")ลองนี้

ดาวน์โหลดได้ที่นี่: http://mvnrepository.com/artifact/org.codehaus.jettison/jettison


ทางออกที่ดีที่สุดแน่นอน! ขอบคุณ
Lastnico

แต่นี่ไม่ได้หมายถึงเครื่องหมายวงเล็บปีกกาเช่น [{
Sergei

1
@Sergei คุณไม่จำเป็นต้องหลบเลี่ยงวงเล็บปีกกาข้างในสตริง JSON
Yobert

อาจเป็นประโยชน์ในการแสดงให้เห็นว่าสิ่งนี้จริงผลตอบแทน
เทรเวอร์

2
org.json.JSONObject.quote ("สตริง json ของคุณ") ก็ใช้งานได้ดี
webjockey

23

org.json.simple.JSONObject.escape () หนีราคา, \, /, \ r, \ n, \ b, \ f, \ t และอักขระควบคุมอื่น ๆ มันสามารถใช้เพื่อหลบหนีรหัส JavaScript

import org.json.simple.JSONObject;
String test =  JSONObject.escape("your string");

3
มันขึ้นอยู่กับไลบรารี json ที่คุณใช้ (JSONObject.escape, JSONObject.quote, .. ) แต่มันเป็นวิธีการแบบคงที่ที่ใช้ในการทำงาน quoting และควรจะนำมาใช้ใหม่
amine

ไลบรารีใดที่ org.json เป็นส่วนหนึ่งของ ฉันไม่มีมันใน classpath ของฉัน
Alex Spurling


22

Apache Commons lang สนับสนุนสิ่งนี้แล้ว เพียงตรวจสอบให้แน่ใจว่าคุณมี Apache Commons รุ่นล่าสุดเพียงพอบนพา ธ คลาสของคุณ คุณจะต้องใช้เวอร์ชัน 3.2+

บันทึกย่อประจำรุ่นสำหรับ 3.2

LANG-797: เพิ่ม escape / unescapeJson ไปที่ StringEscapeUtils


นี่เป็นคำตอบที่ใช้ได้จริงสำหรับฉัน โครงการส่วนใหญ่ใช้ apache คอมมอนส์ lang แล้วดังนั้นไม่จำเป็นต้องเพิ่มการพึ่งพาสำหรับหนึ่งฟังก์ชัน ผู้สร้าง JSON อาจเป็นคำตอบที่ดีที่สุด
absmiths

จากการติดตามและเนื่องจากฉันไม่สามารถหาวิธีแก้ไขความคิดเห็นได้ฉันจึงเพิ่มสิ่งใหม่ฉันจึงพบ javax.json.JsonObjectBuilder และ javax.json.JsonWriter ชุดเครื่องมือสร้าง / รวมนักเขียนที่ดีมาก
absmiths

1
นี้จะเลิกใน Apache คอมมอน lang, คุณจำเป็นต้องใช้ Apache คอมมอนข้อความ น่าเศร้าที่ห้องสมุดนี้ทำตามข้อกำหนดทางเลือก / ล้าสมัยโดยการหลีกเลี่ยง/อักขระ สิ่งนี้แบ่งสิ่งต่างๆมากมายรวมถึง JSON ด้วย URL ในนั้น ข้อเสนอเดิมมี/ลักษณะเป็นถ่านพิเศษที่จะหลบหนี แต่นี่ไม่ใช่กรณีอีกต่อไปอย่างที่เราเห็นในสเป็คล่าสุด ณ เวลาที่เขียน
adamnfish

10

org.json.JSONObject quote(String data) วิธีการทำงาน

import org.json.JSONObject;
String jsonEncodedString = JSONObject.quote(data);

แยกจากเอกสาร:

เข้ารหัสข้อมูลเป็นสตริง JSON นี้ใช้คำพูดและตัวอักษรใด ๆ ที่จำเป็นการหลบหนี [... ] ค่า Null จะถูกตีความเป็นสตริงว่าง


1
org.apache.sling.commons.json.JSONObjectมีสิ่งเดียวกันนี้ด้วย
Jordan Shurmer

5

StringEscapeUtils.escapeJavaScript/ StringEscapeUtils.escapeEcmaScriptควรทำกลอุบายด้วย


10
escapeJavaScriptหนีเครื่องหมายคำพูดเดี่ยวเป็น\'ซึ่งไม่ถูกต้อง
laurt

4

หากคุณกำลังใช้แจ็คสัน fastexml คุณสามารถใช้สิ่งต่อไปนี้: com.fasterxml.jackson.core.io.JsonStringEncoder.getInstance().quoteAsString(input)

หากคุณใช้ codehaus jackson คุณสามารถใช้สิ่งต่อไปนี้: org.codehaus.jackson.io.JsonStringEncoder.getInstance().quoteAsString(input)


3

ไม่แน่ใจว่าคุณหมายถึงอะไรโดย "สร้าง json ด้วยตนเอง" แต่คุณสามารถใช้บางอย่างเช่น gson ( http://code.google.com/p/google-gson/ ) และนั่นจะเปลี่ยน HashMap, Array, String ของคุณ ฯลฯ เป็นค่า JSON ฉันขอแนะนำให้ไปกับกรอบสำหรับสิ่งนี้


2
โดยตนเองฉันหมายถึงไม่ได้ใช้ไลบรารี JSON อย่าง Simple JSON, Gson หรือ XStream
Behrang Saeedzadeh

เป็นเรื่องของความอยากรู้อยากเห็น - ทำไมคุณไม่ต้องการใช้หนึ่งใน API เหล่านี้ มันเหมือนกับการพยายามหลีกเลี่ยง URL ด้วยตนเองแทนที่จะใช้ URLEncode / Decode ...
Vladimir

1
ไม่เหมือนกันไลบรารีเหล่านั้นมาพร้อมกับ URLEncode / Decode ที่เทียบเท่ากันมากมายพวกมันมีแพคเกจการทำให้เป็นอนุกรมทั้งหมดเพื่อให้สามารถคงอยู่ของวัตถุ java ในรูปแบบ json และบางครั้งคุณจำเป็นต้องเข้ารหัสข้อความสั้น ๆ เท่านั้น
jmd

2
ทำด้วยตนเองในการสร้าง JSON เหมาะสมถ้าคุณไม่ต้องการรวมไลบรารีเพื่อซีเรียลบิตข้อมูลขนาดเล็ก
Aditya Kumar Pandey

2
ฉันจะขอให้สมาชิกในทีมนำออกจากโครงการใด ๆ ที่ฉันใช้ถ้าพวกเขากล้าที่จะสร้าง JSON ด้วยตนเองซึ่งมีห้องสมุดคุณภาพสูงให้ทำเช่นนั้น
Michael Joyce

2

ฉันไม่ได้ใช้เวลาเพื่อทำให้แน่ใจ 100% แต่มันใช้งานได้กับอินพุตของฉันมากพอที่จะยอมรับโดยผู้ตรวจสอบ JSON ออนไลน์

org.apache.velocity.tools.generic.EscapeTool.EscapeTool().java("input")

แม้ว่ามันจะดูไม่ดีไปกว่านี้ org.codehaus.jettison.json.JSONObject.quote("your string")

ฉันเพิ่งใช้เครื่องมือความเร็วในโครงการของฉันอยู่แล้ว - อาคาร "manual JSON" ของฉันอยู่ในเทมเพลตความเร็ว


2

สำหรับผู้ที่มาที่นี่กำลังมองหาโซลูชันบรรทัดคำสั่งเช่นเดียวกับฉัน cURL's --data-urlencode ทำงานได้ดี:

curl -G -v -s --data-urlencode 'query={"type" : "/music/artist"}' 'https://www.googleapis.com/freebase/v1/mqlread'

ส่ง

GET /freebase/v1/mqlread?query=%7B%22type%22%20%3A%20%22%2Fmusic%2Fartist%22%7D HTTP/1.1

, ตัวอย่างเช่น. ข้อมูล JSON ที่มีขนาดใหญ่กว่าสามารถใส่ในไฟล์และคุณจะใช้ @ ไวยากรณ์เพื่อระบุไฟล์ที่จะ slurp ในข้อมูลที่จะหนีจาก ตัวอย่างเช่นถ้า

$ cat 1.json 
{
  "type": "/music/artist",
  "name": "The Police",
  "album": []
}

คุณจะใช้

curl -G -v -s --data-urlencode query@1.json 'https://www.googleapis.com/freebase/v1/mqlread'

และตอนนี้ก็เป็นแบบฝึกหัดเกี่ยวกับวิธีการค้นหา Freebase จากบรรทัดคำสั่ง :-)


2

ใช้คลาส EscapeUtils ใน API ภาษาสามัญ

EscapeUtils.escapeJavaScript("Your JSON string");

1
โปรดทราบว่าคำพูดเดียวเช่นได้รับการจัดการที่แตกต่างกันเมื่อหนีไป javascript หรือ json ใน commons.lang 3.4 StringEscapeUtils ( commons.apache.org/proper/commons-lang/javadocs/api-3.4/org/ ...... ) มีวิธี escapeJSON ซึ่งแตกต่างจากเมธอด escapeJavaScript ใน commons.lang 2: commons.apache org / เหมาะสม / commons-lang / javadocs / api-2.6 / org / …
GlennV

1

พิจารณาคลาสJsonWriterของMoshi มันมี API ที่ยอดเยี่ยมและลดการคัดลอกให้น้อยที่สุดทุกอย่างสามารถสตรีมไปยัง Filed, OutputStream และอื่น ๆ ได้เป็นอย่างดี

OutputStream os = ...;
JsonWriter json = new JsonWriter(Okio.buffer(Okio.sink(os)));
json.beginObject();
json.name("id").value(getId());
json.name("scores");
json.beginArray();
for (Double score : getScores()) {
  json.value(score);
}
json.endArray();
json.endObject();

หากคุณต้องการสตริงในมือ:

Buffer b = new Buffer(); // okio.Buffer
JsonWriter writer = new JsonWriter(b);
//...
String jsonString = b.readUtf8();


0

หากคุณต้องการหลบหนี JSON ภายในสตริง JSON ให้ใช้ org.json.JSONObject.quote ("สตริง json ของคุณที่ต้องหนี") ดูเหมือนจะทำงานได้ดี


0

การใช้ไวยากรณ์ \ uXXXX สามารถแก้ปัญหานี้ได้ Google UTF-16 ที่มีชื่อของเครื่องหมายคุณสามารถค้นหา XXXX ได้ตัวอย่างเช่น: utf-16 เครื่องหมายคำพูดคู่


0

วิธีการที่แสดงการใช้งานจริงนั้นเป็นความผิดพลาดทั้งหมด
ฉันไม่มีรหัส Java แต่สำหรับบันทึกคุณสามารถแปลงรหัส C # นี้ได้อย่างง่ายดาย:

ความอนุเคราะห์จาก mono-project @ https://github.com/mono/monb/master/mcs/class/System.Web/System.Web/HttpUtility.cs

public static string JavaScriptStringEncode(string value, bool addDoubleQuotes)
{
    if (string.IsNullOrEmpty(value))
        return addDoubleQuotes ? "\"\"" : string.Empty;

    int len = value.Length;
    bool needEncode = false;
    char c;
    for (int i = 0; i < len; i++)
    {
        c = value[i];

        if (c >= 0 && c <= 31 || c == 34 || c == 39 || c == 60 || c == 62 || c == 92)
        {
            needEncode = true;
            break;
        }
    }

    if (!needEncode)
        return addDoubleQuotes ? "\"" + value + "\"" : value;

    var sb = new System.Text.StringBuilder();
    if (addDoubleQuotes)
        sb.Append('"');

    for (int i = 0; i < len; i++)
    {
        c = value[i];
        if (c >= 0 && c <= 7 || c == 11 || c >= 14 && c <= 31 || c == 39 || c == 60 || c == 62)
            sb.AppendFormat("\\u{0:x4}", (int)c);
        else switch ((int)c)
            {
                case 8:
                    sb.Append("\\b");
                    break;

                case 9:
                    sb.Append("\\t");
                    break;

                case 10:
                    sb.Append("\\n");
                    break;

                case 12:
                    sb.Append("\\f");
                    break;

                case 13:
                    sb.Append("\\r");
                    break;

                case 34:
                    sb.Append("\\\"");
                    break;

                case 92:
                    sb.Append("\\\\");
                    break;

                default:
                    sb.Append(c);
                    break;
            }
    }

    if (addDoubleQuotes)
        sb.Append('"');

    return sb.ToString();
}

สามารถอัดลงในนี้

    // https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs
public class SimpleJSON
{

    private static  bool NeedEscape(string src, int i)
    {
        char c = src[i];
        return c < 32 || c == '"' || c == '\\'
            // Broken lead surrogate
            || (c >= '\uD800' && c <= '\uDBFF' &&
                (i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF'))
            // Broken tail surrogate
            || (c >= '\uDC00' && c <= '\uDFFF' &&
                (i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF'))
            // To produce valid JavaScript
            || c == '\u2028' || c == '\u2029'
            // Escape "</" for <script> tags
            || (c == '/' && i > 0 && src[i - 1] == '<');
    }



    public static string EscapeString(string src)
    {
        System.Text.StringBuilder sb = new System.Text.StringBuilder();

        int start = 0;
        for (int i = 0; i < src.Length; i++)
            if (NeedEscape(src, i))
            {
                sb.Append(src, start, i - start);
                switch (src[i])
                {
                    case '\b': sb.Append("\\b"); break;
                    case '\f': sb.Append("\\f"); break;
                    case '\n': sb.Append("\\n"); break;
                    case '\r': sb.Append("\\r"); break;
                    case '\t': sb.Append("\\t"); break;
                    case '\"': sb.Append("\\\""); break;
                    case '\\': sb.Append("\\\\"); break;
                    case '/': sb.Append("\\/"); break;
                    default:
                        sb.Append("\\u");
                        sb.Append(((int)src[i]).ToString("x04"));
                        break;
                }
                start = i + 1;
            }
        sb.Append(src, start, src.Length - start);
        return sb.ToString();
    }
}

วิธีquote()การอธิบายไว้ในคำตอบอื่น ๆ ผิดพลาดอย่างไร?
แซนดี้

0

ฉันคิดว่าคำตอบที่ดีที่สุดในปี 2560 คือใช้ javax.json API ใช้ javax.json.JsonBuilderFactory เพื่อสร้างออบเจ็กต์ json ของคุณจากนั้นเขียนออบเจกต์โดยใช้ javax.json.JsonWriterFactory ชุดเครื่องมือสร้าง / เขียนที่ดีมาก

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