Tôi nên thoát chuỗi trong JSON như thế nào?


154

Khi tạo dữ liệu JSON theo cách thủ công, tôi nên thoát các trường chuỗi như thế nào? Tôi có nên sử dụng một cái gì đó như Apache Commons Lang StringEscapeUtilities.escapeHtml, StringEscapeUtilities.escapeXmlhay tôi nên sử dụng java.net.URLEncoder?

Vấn đề là khi tôi sử dụng SEU.escapeHtml, nó không thoát được dấu ngoặc kép và khi tôi quấn toàn bộ chuỗi trong một cặp 's, một JSON không đúng định dạng sẽ được tạo.


20
Nếu bạn gói toàn bộ chuỗi thành một cặp ', bạn sẽ phải chịu số phận từ đầu: Chuỗi JSON chỉ có thể được bao quanh ". Xem ietf.org/rfc/rfc4627.txt .
Thanatos

2
+1 cho StringEscapeUtilitiesphác thảo. Nó khá hữu ích.
Muhammad Gelbana

Câu trả lời:


157

Lý tưởng nhất là tìm một thư viện JSON bằng ngôn ngữ của bạn để bạn có thể cung cấp một số cấu trúc dữ liệu phù hợp và để nó lo lắng về cách thoát khỏi mọi thứ . Nó sẽ giữ cho bạn nhiều vệ sinh hơn. Nếu vì bất kỳ lý do gì bạn không có thư viện bằng ngôn ngữ của mình, bạn không muốn sử dụng thư viện này (tôi sẽ không đề xuất điều này), hoặc bạn đang viết thư viện JSON, hãy đọc tiếp.

Thoát khỏi nó theo RFC. JSON là khá tự do: Các nhân vật duy nhất bạn phải thoát được \, "và các mã điều khiển (bất cứ điều gì ít hơn U + 0020).

Cấu trúc thoát này là đặc trưng cho JSON. Bạn sẽ cần một hàm cụ thể JSON. Tất cả các lối thoát có thể được viết dưới dạng đơn \uXXXXvị XXXXmã UTF-16¹ cho ký tự đó. Có một vài phím tắt, chẳng hạn như \\, cũng hoạt động. (Và chúng dẫn đến một đầu ra nhỏ hơn và rõ ràng hơn.)

Để biết chi tiết đầy đủ, xem RFC .

Lối thoát của SONJSON được xây dựng trên JS, do đó, nó sử dụng \uXXXX, XXXXđơn vị mã UTF-16 ở đâu. Đối với các điểm mã bên ngoài BMP, điều này có nghĩa là mã hóa các cặp thay thế, có thể có một chút lông. (Hoặc, bạn chỉ có thể xuất ký tự trực tiếp, vì mã hóa của JSON là văn bản Unicode và cho phép các ký tự cụ thể này.)


Nó có hợp lệ trong JSON, như trong JavaScript, để đặt các chuỗi trong dấu ngoặc kép hoặc dấu ngoặc đơn không? Hoặc nó chỉ có giá trị để gửi chúng trong dấu ngoặc kép?
Behrang Saeedzadeh

14
Chỉ trích dẫn kép ( ").
Thanatos

3
@Sergei: Các ký tự {[]}:?không được thoát bằng một dấu gạch chéo ngược. ( \:ví dụ, không hợp lệ trong chuỗi JSON.) Tất cả những thứ đó có thể được thoát bằng cách sử dụng \uXXXXcú pháp, lãng phí một vài byte. Xem §2,5 của RFC.
Thanatos

2
Tôi không chắc nó được hỗ trợ rộng rãi như thế nào, nhưng theo kinh nghiệm của tôi, một cuộc gọi để JSON.stringify()thực hiện công việc.
LS

2
@BitTickler một ký tự unicode hoàn toàn không mơ hồ - nó chỉ có nghĩa là nó có một điểm mã (hoặc điểm) trong thông số unicode. Khi bạn sử dụng std :: string, đó là một loạt các ký tự unicode. Khi bạn cần tuần tự hóa nó, hãy nói với một tệp hoặc trên mạng, đó là nơi 'mã hóa' xuất hiện. Có vẻ như theo Thanatos rằng họ muốn bạn sử dụng UTF, nhưng về mặt kỹ thuật, mọi mã hóa đều có thể được sử dụng miễn là nó có thể được hoàn nguyên thành các ký tự unicode.
Gerard ONeill

54

Trích xuất từ 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
Chà, đây là thẻ OP
MonoThreaded

Không hiểu chỉ khi c <'', thay đổi thành \ u. Trong trường hợp của tôi, có ký tự \ uD38D, từ 55357 trở lên '', vì vậy không thay đổi thành \ u ...
Stony

1
@Stony Nghe có vẻ như một câu hỏi mới
MonoThreaded

@MonoThreaded Cảm ơn câu trả lời của bạn, tôi vẫn không biết tại sao. nhưng cuối cùng, tôi đã thay đổi phương thức để sửa nó như bên dưới, if (c <'' || c> 0x7f) {t = "000" + Integer.toHexString (c) .toUpperCase (); sb.append ("\\ u" + t.sub chuỗi (t.length () - 4)); } khác {sb.append (c); }}
Stony

1
@Stony, tất cả các nhân vật khác hơn ", \ và ký tự điều khiển (những người trước “”) là chuỗi bên trong JSON hợp lệ dưới dạng kết hợp miễn là mã hóa đầu ra. Nói cách khác, bạn không cần phải mã hóa Hồi giáo 펍 \uD38Dmiễn là mã hóa UTF được bảo toàn.
meustrus

37

Hãy thử điều này org.codehaus.jettison.json.JSONObject.quote("your string").

Tải xuống tại đây: http://mvnreposective.com/artifact/org.codehaus.jettison/jettison


Chắc chắn là giải pháp tốt nhất! Thx
Lastnico

nhưng điều này không trích dẫn các niềng răng như [{
Sergei

1
@Sergei Bạn không cần phải thoát dấu ngoặc trong chuỗi JSON.
Yobert

Có thể hữu ích để hiển thị những gì thực sự trở lại.
Trevor

2
org.json.JSONObject.quote ("chuỗi json của bạn") cũng hoạt động tốt
webj Racer

23

org.json.simple.JSONObject.escape () thoát dấu ngoặc kép, \, /, \ r, \ n, \ b, \ f, \ t và các ký tự điều khiển khác. Nó có thể được sử dụng để thoát mã JavaScript.

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

3
Nó phụ thuộc vào thư viện json bạn đang sử dụng (JSONObject.escape, JSONObject.quote, ..) nhưng nó luôn luôn là một phương thức tĩnh thực hiện công việc trích dẫn và đơn giản nên được sử dụng lại
amine

Thư viện nào là một phần của org.json? Tôi không có nó trên classpath của tôi.
Alex Spurling


22

Apache commons lang hiện hỗ trợ này. Chỉ cần đảm bảo rằng bạn có một phiên bản Apache commons lang đủ gần đây trên đường dẫn lớp của bạn. Bạn sẽ cần phiên bản 3.2+

Ghi chú phát hành cho phiên bản 3.2

LANG-797: Đã thêm esc / unescapeJson vào StringEscapeUtils.


Đây là câu trả lời thiết thực nhất cho tôi. Hầu hết các dự án đã sử dụng apache commons lang, vì vậy không cần thêm phụ thuộc cho một chức năng. Một trình xây dựng JSON có lẽ sẽ là câu trả lời tốt nhất.
thợ rèn

Để theo dõi và vì tôi không thể tìm ra cách chỉnh sửa nhận xét Tôi đã thêm một nhận xét mới, tôi đã tìm thấy javax.json.JsonObjectBuilder và javax.json.JsonWriter. Kết hợp xây dựng / nhà văn rất tốt đẹp.
thợ rèn

1
Điều này không được dùng trong apache commons lang, bạn cần sử dụng văn bản apache commons . Đáng buồn thay, thư viện này theo thông số kỹ thuật tùy chọn / lỗi thời bằng cách thoát các /ký tự. Điều này phá vỡ nhiều thứ bao gồm JSON có URL trong đó. Đề xuất ban đầu có /một char đặc biệt để trốn thoát nhưng đây không còn là vấn đề nữa, như chúng ta có thể thấy trong thông số mới nhất tại thời điểm viết bài
adamnfish 27/03/18

10

org.json.JSONObject quote(String data) phương pháp làm công việc

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

Trích xuất từ ​​tài liệu:

Mã hóa dữ liệu dưới dạng chuỗi JSON. Điều này áp dụng dấu ngoặc kép và bất kỳ nhân vật cần thiết thoát . [...] Null sẽ được hiểu là một chuỗi rỗng


1
org.apache.sling.commons.json.JSONObjectcũng có điều tương tự
Jordan Shurmer

5

StringEscapeUtils.escapeJavaScript/ StringEscapeUtils.escapeEcmaScriptnên làm thủ thuật quá.


10
escapeJavaScriptthoát khỏi dấu ngoặc đơn \'là không chính xác.
nguyệt quế

4

Nếu bạn đang sử dụng jackson fastexml, bạn có thể sử dụng như sau: com.fasterxml.jackson.core.io.JsonStringEncoder.getInstance().quoteAsString(input)

Nếu bạn đang sử dụng codehaus jackson, bạn có thể sử dụng như sau: org.codehaus.jackson.io.JsonStringEncoder.getInstance().quoteAsString(input)


3

Không chắc ý của bạn là gì khi "tạo json thủ công", nhưng bạn có thể sử dụng cái gì đó như gson ( http://code.google.com.vn/p/google-gson/ ) và điều đó sẽ biến đổi HashMap, Mảng, Chuỗi, v.v. , đến một giá trị JSON. Tôi khuyên bạn nên đi với một khuôn khổ cho việc này.


2
Bằng tay tôi có nghĩa là không sử dụng thư viện JSON như Simple JSON, Gson hoặc XStream.
Behrang Saeedzadeh

Chỉ là vấn đề tò mò - tại sao bạn không muốn sử dụng một trong những API này? Giống như cố gắng thoát URL theo cách thủ công, thay vì sử dụng URLEncode / Giải mã ...
Vladimir

1
Không thực sự giống nhau, các thư viện đó đi kèm nhiều hơn so với URLEncode / Giải mã, chúng bao gồm toàn bộ gói tuần tự hóa để cho phép duy trì đối tượng java ở dạng json và đôi khi bạn thực sự chỉ cần mã hóa một bó văn bản ngắn
jmd

2
thực hiện việc tạo JSON thủ công có ý nghĩa, nếu bạn muốn không bao gồm một thư viện chỉ để tuần tự hóa các bit dữ liệu nhỏ
Aditya Kumar Pandey

2
Tôi sẽ yêu cầu xóa một thành viên trong nhóm khỏi bất kỳ dự án nào tôi tham gia nếu họ dám tạo JSON theo cách thủ công, nơi có thư viện chất lượng cao để làm như vậy.
Michael Joyce

2

Tôi đã không dành thời gian để chắc chắn 100%, nhưng nó đã hoạt động cho các đầu vào của tôi đủ để được các trình xác nhận JSON trực tuyến chấp nhận:

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

mặc dù nó trông không đẹp hơn org.codehaus.jettison.json.JSONObject.quote("your string")

Tôi chỉ đơn giản là sử dụng các công cụ vận tốc trong dự án của mình - tòa nhà "JSON thủ công" của tôi nằm trong một mẫu vận tốc


2

Đối với những người đến đây để tìm kiếm một giải pháp dòng lệnh, như tôi, --data-urlencode của cURL hoạt động tốt:

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

gửi

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

, ví dụ. Dữ liệu JSON lớn hơn có thể được đặt trong một tệp và bạn sẽ sử dụng cú pháp @ để chỉ định một tệp để ẩn trong dữ liệu cần thoát. Ví dụ, nếu

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

bạn sẽ sử dụng

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

Và bây giờ, đây cũng là một hướng dẫn về cách truy vấn Freebase từ dòng lệnh :-)


2

Sử dụng lớp EscapeUtils trong API commons lang.

EscapeUtils.escapeJavaScript("Your JSON string");

1
Lưu ý rằng các trích dẫn đơn lẻ chẳng hạn được xử lý khác nhau khi thoát sang javascript hoặc json. Trong commons.lang 3.4 StringEscapeUtils ( commons.apache.org/proper/commons-lang/javadocs/api-3.4/org/... ) có một phương pháp escapeJSON đó là khác biệt so với các phương pháp escapeJavaScript trong commons.lang 2: commons.apache. org / thích hợp / commons-lang / javadocs / api-2.6 / org /
Lỗi

1

Hãy xem xét lớp JsonWriter của Moshi . Nó có một API tuyệt vời và nó giảm việc sao chép đến mức tối thiểu, mọi thứ đều có thể được truyền trực tiếp đến một tệp, OutputStream, v.v.

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

Nếu bạn muốn có chuỗi trong tay:

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


0

Nếu bạn cần thoát JSON trong chuỗi JSON, hãy sử dụng org.json.JSONObject.quote ("chuỗi json của bạn cần được thoát") dường như hoạt động tốt


0

sử dụng cú pháp \ uXXXX có thể giải quyết vấn đề này, google UTF-16 với tên của dấu hiệu, bạn có thể tìm hiểu XXXX, ví dụ: trích dẫn kép utf-16


0

Các phương pháp ở đây cho thấy việc thực hiện thực tế đều bị lỗi.
Tôi không có mã Java, nhưng chỉ để ghi lại, bạn có thể dễ dàng chuyển đổi mã C # này:

Lịch sự của dự án đơn sắc @ https://github.com/mono/mono/blob/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();
}

Điều này có thể được nén vào

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

Làm thế nào là quote()phương pháp được mô tả trong các câu trả lời khác bị lỗi?
Sandy

0

Tôi nghĩ rằng câu trả lời tốt nhất trong năm 2017 là sử dụng API javax.json. Sử dụng javax.json.JsonBuilderFactory để tạo các đối tượng json của bạn, sau đó viết các đối tượng ra bằng cách sử dụng javax.json.JsonWriterFactory. Kết hợp xây dựng / nhà văn rất tốt đẹp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.