如何在JSON中转义字符串?


154

手动创建JSON数据时,应如何转义字符串字段?我应该用什么样的Apache Commons Lang中的StringEscapeUtilities.escapeHtmlStringEscapeUtilities.escapeXml或者我应该使用java.net.URLEncoder

问题是,当我使用时SEU.escapeHtml,它不会转义引号,并且当我将整个字符串包装在一对's中时,将生成格式错误的JSON。


20
如果将整个字符串包装为一对',那么从一开始就注定要失败:JSON字符串只能用包围"。参见ietf.org/rfc/rfc4627.txt
塔纳托斯

2
+1为StringEscapeUtilities大纲。它非常有用。
Muhammad Gelbana '11年

Answers:


157

理想情况下,找到一种使用您的语言的JSON库,您可以向该提供一些适当的数据结构,并使它担心如何转义。它会让您保持头脑清醒。如果出于某种原因您没有使用您的语言的库,不想使用一个库(我不建议这样做¹),或者您正在编写JSON库,请继续阅读。

根据RFC对其进行转义。JSON是相当宽松:你唯一的字符必须逃脱是\"和控制代码(东西不是U + 0020以下)。

这种转义结构特定于JSON。您将需要特定于JSON的功能。所有逃逸的可以写成\uXXXX其中XXXX是该字符的UTF-16代码unit¹。有一些快捷方式也可以使用,例如\\。(它们会导致更小,更清晰的输出。)

有关详细信息,请参见RFC

¹JSON的转义是基于JS构建的,因此它使用\uXXXX,其中XXXX为UTF-16代码单元。对于BMP之外的代码点,这意味着对代理对进行编码,这可能会有些毛茸茸。(或者,您可以直接输出字符,因为JSON的编码是Unicode文本,并且允许这些特殊字符。)


在JSON中(如在JavaScript中)将字符串括在双引号或单引号中是否有效?还是只用双引号将它们括起来才有效?
Behrang Saeedzadeh,2010年

14
仅双引号(")。
塔纳托斯

3
@Sergei:字符{[]}:?不能以单个反斜杠转义。(\:例如,在JSON字符串中无效)。所有这些都可以选择使用\uXXXX语法进行转义,从而浪费了几个字节。请参阅RFC的第2.5节。
Thanatos 2014年

2
我不确定它的支持范围如何,但是根据我的经验,我呼吁进行JSON.stringify()这项工作。
LS 2014年

2
@BitTickler unicode字符一点都不模糊-只是表示它在unicode规范中具有一个或多个代码点。当您使用std :: string时,它是一堆unicode字符。当您需要对其进行序列化时,可以说一个文件或通过网络,这就是“编码”的来源。根据Thanatos的说法,他们希望您使用UTF,但从技术上讲,只要可以使用任何编码即可。可以将其重构为unicode字符。
Gerard 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 2016年

@MonoThreaded感谢您的回复,我仍然不知道为什么。但最后,如果(c <''|| c> 0x7f){t =“ 000” + Integer.toHexString(c).toUpperCase(); sb.append(“ \\ u” + t.substring(t.length()-4)); }其他{sb.append(c); }
斯托尼(Stony)2013年

1
@Stony,只要输出编码匹配,除"\ 和控制字符(“”之前的那些字符)外的所有其他字符(在“”之前)都是有效的。换句话说,\uD38D只要保留UTF编码,就不需要编码“펍” 。
meustrus


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等),但是它始终是执行引用工作的静态方法,应该简单地重用

org.json是哪个库的一部分?我的班级路径上没有它。
亚历克斯·斯普林


22

Apache commons lang现在支持此功能。只要确保您的类路径上具有最新版本的Apache commons lang。您需要版本3.2+

版本3.2发行说明

LANG-797:在StringEscapeUtils中添加了escape / unescapeJson。


这是对我来说最实用的答案。大多数项目已经使用apache commons lang,因此无需为一个函数添加依赖项。JSON构建器可能是最好的答案。
Absmiths

作为后续,由于无法弄清楚如何编辑注释,我添加了一个新注释,因此找到了javax.json.JsonObjectBuilder和javax.json.JsonWriter。非常好的构建器/编写器组合。
Absmiths

1
在apache commons lang中已弃用,您需要使用apache commons text。可悲的是,该库通过转义/字符来遵循可选/过时的规范。这会破坏很多东西,包括其中带有URL的JSON。最初的建议是/作为特殊字符逃脱的,但是情况已不再如此,正如我们在撰写本文时的最新规范中所
-Adamnfish

10

org.json.JSONObject quote(String data) 方法胜任

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

从文档中摘录:

将数据编码为JSON字符串。这适用于引号和任何必要的字符转义。[...]空将被解释为空字符串


1
org.apache.sling.commons.json.JSONObject也有同样的事情
Jordan Shurmer '16

5

StringEscapeUtils.escapeJavaScript/也StringEscapeUtils.escapeEcmaScript应该做到这一点。


10
escapeJavaScript将单引号转义为\',这是不正确的。
2014年

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,2010年

出于好奇,您为什么不想要使用这些API之一?这就像尝试手动转义URL,而不是使用URLEncode / Decode ...
Vladimir 2010年

1
并非完全相同,这些库比URLEncode / Decode具有更多功能,它们包含一个完整的序列化程序包,以允许json形式的java对象持久存在,有时您实际上只需要编码一小段文本
jmd 2011年

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")

我已经在项目中使用了速度工具-我的“手动JSON”构建位于速度模板中


2

对于像我这样来这里寻求命令行解决方案的用户,cURL的--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的教程:-)



1

考虑MoshiJsonWriter类。它具有出色的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#代码:

由mono项目提供@ 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年最好的答案是使用javax.json API。使用javax.json.JsonBuilderFactory创建json对象,然后使用javax.json.JsonWriterFactory将对象写出。非常好的构建器/编写器组合。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.