Как мне избежать строк в JSON?


154

Как вручную создавать строковые поля при создании данных JSON? Должен ли я использовать что - то вроде Apache Commons Ланг StringEscapeUtilities.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. Все escape-коды могут быть записаны как \uXXXXгде XXXXнаходится кодовая единица UTF-16 для этого символа. Есть несколько сочетаний клавиш, например \\, которые также работают. (И они приводят к меньшему и более четкому выводу.)

Для получения полной информации см. RFC .

Экранирование ¹JSON построено на JS, поэтому он использует \uXXXX, где XXXXэто код блок UTF-16. Для кодовых точек вне BMP это означает кодирование суррогатных пар, что может быть немного затруднительно. (Или вы можете просто вывести символ напрямую, так как кодированный для JSON символ Unicode позволяет использовать эти символы.)


Допустимо ли в JSON, как и в JavaScript, заключать строки в двойные или одинарные кавычки? Или допустимо заключать их в двойные кавычки?
Бехранг Саидзаде

14
Только двойные кавычки ( ").
Танатос

3
@Sergei: Символы {[]}:?не должны быть экранированы одной обратной косой чертой. ( \:Например, недопустимо в строке JSON.) Все из них могут быть необязательно экранированы с использованием \uXXXXсинтаксиса, тратя несколько байтов. См. §2.5 RFC.
Танатос

2
Я не уверен, насколько широко это поддерживается, но по моему опыту, звонок JSON.stringify()сделал работу.
LS

2
@BitTickler символ Юникода вовсе не расплывчатый - он просто означает, что он имеет кодовую точку (или точки) в спецификации Юникода. Когда вы используете 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
@ Звучит как новый вопрос
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? У меня нет этого на моем classpath.
Алекс Сперлинг


22

Apache Commons Lang теперь поддерживает это. Просто убедитесь, что у вас есть достаточно свежая версия Apache Commons Lang на вашем пути к классам. Вам понадобится версия 3.2+

Примечания к выпуску для версии 3.2

LANG-797: добавлен escape / unescapeJson в StringEscapeUtils.


Это самый практичный ответ для меня. Большинство проектов уже используют Apache Commons Lang, поэтому нет необходимости добавлять зависимость для одной функции. JSON Builder, вероятно, будет лучшим ответом.
Absmiths

В качестве продолжения, и поскольку я не могу понять, как редактировать комментарий, я добавил новый, я нашел javax.json.JsonObjectBuilder и javax.json.JsonWriter. Очень хорошая комбинация строитель / писатель.
Absmiths

1
Это устарело в Apache Commons Lang, вам нужно использовать Apache Commons Text . К сожалению, эта библиотека следует необязательной / устаревшей спецификации, экранируя /символы. Это ломает много вещей, включая 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.
Бехранг Саидзаде

Просто любопытство - почему бы вам не использовать один из этих API? Это все равно, что пытаться экранировать URL-адреса вручную, а не использовать URLEncode / Decode ...
Владимир

1
Не совсем то же самое, эти библиотеки имеют гораздо больше, чем эквивалент URLEncode / Decode, они включают в себя целый пакет сериализации, чтобы обеспечить сохранение java-объекта в форме json, и иногда вам действительно нужно только кодировать небольшой набор текста
JMD

2
ручное создание JSON имеет смысл, если вы не хотите включать библиотеку только для сериализации небольших битов данных
Адитья Кумар Пандей,

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 общего языка.

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