Як слід уникати рядків у JSON?


154

Як створювати дані JSON вручну, як слід уникати рядкових полів? Чи варто використовувати щось на зразок Apache Commons LangStringEscapeUtilities.escapeHtml , StringEscapeUtilities.escapeXmlабо я повинен використовуватиjava.net.URLEncoder ?

Проблема полягає в тому, що коли я використовую SEU.escapeHtml, він не уникає лапок, і коли я загортаю всю нитку в пару 's, буде сформовано неправильний JSON.


20
Якщо ви загортаєте всю нитку в пару ', ви зрештою приречені: рядки JSON можна оточити лише ". Дивіться ietf.org/rfc/rfc4627.txt .
Танатос

2
+1 для StringEscapeUtilitiesконтуру. Це досить корисно.
Мухаммад Гелбана

Відповіді:


157

В ідеалі знайдіть бібліотеку JSON своєю мовою, до якої можна подати якусь відповідну структуру даних, і нехай вона буде хвилюватися про те, як уникнути речей . Це збереже вас набагато безпечніше. Якщо з будь-якої причини у вас немає бібліотеки на вашій мові, ви не хочете використовувати її (я б не пропонував це¹) або ви пишете бібліотеку JSON, читайте далі.

Уникнути цього за даними RFC. JSON досить ліберальний: тільки символи ви повинні вирватися з кабали \, "і керуючі коди (все , що менше , ніж U + 0020).

Ця структура втечі є специфічною для JSON. Вам знадобиться спеціальна функція JSON. Усі вихідні записи можуть бути записані так, \uXXXXде XXXXзнаходиться кодова одиниця UTF-16¹ для цього символу. Є кілька ярликів, таких як \\, які також працюють. (І вони призводять до меншого та чіткішого результату.)

Повна інформація див. У RFC .

EscaJSON втікання побудований на JS, тому він використовує \uXXXX, де XXXXє кодовим блоком UTF-16. Для кодових пунктів поза BMP це означає кодування сурогатних пар, які можуть бути трохи волохатими. (Або ви можете просто вивести символ безпосередньо, оскільки кодований JSON - текст Unicode, і він дозволяє цим конкретним символам.)


Чи дійсно в JSON, як і в JavaScript, для додавання рядків у подвійні лапки або одинарні лапки? Або справедливо лише укладати їх у подвійні лапки?
Behrang Saeedzadeh

14
Лише подвійні лапки ( ").
Танатос

3
@Sergei: персонажів {[]}:?не можна уникати одним нахилом. ( \:наприклад, недійсне в рядку JSON.) Усі вони необов'язково можна уникнути, використовуючи \uXXXXсинтаксис, втрачаючи кілька байтів. Див. §2.5 РФК.
Танатос

2
Я не впевнений, наскільки широко це підтримується, але в моєму досвіді заклик виконати JSON.stringify()цю роботу.
LS

2
@BitTickler символ unicode зовсім не розпливчастий - це просто означає, що він має кодову точку (або точки) у специфікації unicode. Коли ви використовуєте std :: string, це купа символів unicode. Коли вам потрібно його серіалізувати, давайте скажемо у файл або через мережу, саме там надходить "яке кодування". Згідно з Танатосом, схоже, вони хочуть, щоб ви використовували UTF, але технічно будь-яке кодування може використовуватися до тих пір, поки його можна перетворити на символи unicode.
Джерард ONeill

54

Витяг з Джетсісона :

 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, який становить 55357 і більше '', тому не змінюється на \ 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); }}
Стоун

1
@Stony, всі символи, окрім ", \ та контрольні символи (ті, що знаходяться перед “”), дійсні в рядках JSON, до тих пір, поки вихідне кодування збігається. Іншими словами, вам не потрібно кодувати “펍” \uD38D, доки збережене кодування UTF.
meustrus

37

Спробуйте це org.codehaus.jettison.json.JSONObject.quote("your string").

Завантажте його тут: http://mvnrepository.com/artifact/org.codehaus.jettison/jettison


Однозначно найкраще рішення! Thx
Lastnico

але це не цитує дужки на кшталт [{
Сергій

1
@Sergei Вам не доведеться уникати дужок всередині рядка JSON.
Йоберт

Може бути корисним, щоб показати, що це насправді повертає.
Тревор

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, ..), але це завжди статичний метод, що виконує завдання котирування, і його просто потрібно повторно використовувати
amine

До якої бібліотеки входить org.json? Я не маю цього на своєму класі.
Алекс Сперлінг


22

Тепер Apache commons lang підтримує це. Просто переконайтеся, що у вас на останній версії є достатньо недавня версія спільноти Apache. Вам знадобиться версія 3.2+

Примітки до випуску версії 3.2

LANG-797: Додано втечу / unescapeJson до StringEscapeUtils.


Це найпрактичніша відповідь для мене. Більшість проектів вже використовують apache commons lang, тому не потрібно додавати залежність від однієї функції. Найкращою відповіддю може стати будівельник JSON.
абсміти

Як подальший досвід, і оскільки я не можу зрозуміти, як редагувати коментар, я додав новий, я знайшов javax.json.JsonObjectBuilder та javax.json.JsonWriter. Дуже приємне поєднання будівельника та письменника.
абсміти

1
Це застаріло в апаш-спільному лозі, вам потрібно використовувати текст апач-общинця . На жаль, ця бібліотека слідує за необов'язковою / застарілою специфікацією, уникаючи /символів. Це порушує багато речей, включаючи 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теж є те саме
Джордан Шурмер

5

StringEscapeUtils.escapeJavaScript/ теж StringEscapeUtils.escapeEcmaScriptповинен зробити трюк.


10
escapeJavaScriptуникає одинарних лапок як \', що невірно.
laurt

4

Якщо ви використовуєте fastexml jackson, ви можете використовувати наступне: 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 / декодування ...
Володимир

1
Насправді це не те, що ці бібліотеки одержують набагато більше, ніж еквівалент URLEncode / Decode, вони включають цілий пакет серіалізації, що дозволяє зберегти об'єкт java у формі json, а іноді вам потрібно лише кодувати короткий куточок тексту
jmd

2
зробити вручну створення JSON має сенс, якщо ви не хочете включати бібліотеку лише для серіалізації невеликих біт даних
Aditya Kumar Pandey

2
Я б попросив, щоб член команди був видалений з будь-якого проекту, на якому я працював, якщо вони наважуються створити JSON вручну там, де існувала високоякісна бібліотека для цього.
Майкл Джойс

2

Я не витратив час, щоб зробити 100% впевненим, але це працювало для моїх вкладів, щоб прийняти онлайн-валідатори JSON:

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

хоча це виглядає не краще org.codehaus.jettison.json.JSONObject.quote("your string")

Я просто використовую інструменти швидкості вже в своєму проекті - моя "ручна 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 можна вводити у файл, і ви використовуєте синтаксис @, щоб вказати, до якого файлу потрібно перенести дані. Наприклад, якщо

$ 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 API Commons.

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

Розглянемо Moshi «s JsonWriter клас. Він має чудовий API, і він зводить копіювання до мінімуму, все можна красиво передавати у файл, 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 #-код:

Людство монопроекту @ 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();
}

Це можна ущільнити

    // 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

Я думаю, найкраща відповідь у 2017 році - використовувати API javax.json. Використовуйте javax.json.JsonBuilderFactory, щоб створити свої об'єкти json, а потім випишіть їх за допомогою javax.json.JsonWriterFactory. Дуже приємне поєднання будівельника та письменника.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.