Як слід уникати рядків у 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.