ChatGPT解决这个技术问题 Extra ChatGPT

我应该如何在 JSON 中转义字符串?

手动创建 JSON 数据时,我应该如何转义字符串字段?我应该使用 Apache Commons Lang 的 StringEscapeUtilities.escapeHtmlStringEscapeUtilities.escapeXml 之类的东西,还是应该使用 java.net.URLEncoder

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

如果您将整个字符串包装在一对 ' 中,那么您从一开始就注定要失败:JSON 字符串只能用 " 包围。请参阅ietf.org/rfc/rfc4627.txt
StringEscapeUtilities 大纲 +1。它非常有用。

T
Thanatos

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

根据 RFC 对其进行转义。 JSON 非常自由:您必须转义的唯一字符是 \" 和控制代码(小于 U+0020 的任何字符)。

这种转义结构是 JSON 特有的。你需要一个 JSON 特定的函数。所有转义都可以写成 \uXXXX,其中 XXXX 是该字符的 UTF-16 代码单元¹。有一些快捷方式,例如 \\,也可以使用。 (它们会产生更小更清晰的输出。)

有关完整的详细信息,请参阅 the RFC

¹JSON 的转义是建立在 JS 之上的,所以它使用 \uXXXX,其中 XXXX 是一个 UTF-16 代码单元。对于 BMP 之外的代码点,这意味着编码代理对,这可能会有点麻烦。 (或者,您可以直接输出字符,因为 JSON 编码为 Unicode 文本,并允许这些特定字符。)


在 JSON 中是否有效,如在 JavaScript 中,将字符串用双引号或单引号括起来?还是仅将它们括在双引号中才有效?
@Sergei:字符 {[]}:? 不能用单个反斜杠转义。 (例如,\: 在 JSON 字符串中无效。)所有这些都可以使用 \uXXXX 语法进行转义,但会浪费几个字节。请参阅 RFC 的第 2.5 节。
我不确定它的支持范围有多广,但根据我的经验,调用 JSON.stringify() 可以完成这项工作。
对于我的小脑袋来说,RFC 有点含糊,当它声明“......任何 UNICODE 字符......”时。哪种编码? utf-8、utf-16、shift-jis、...?大端/小端? RFC 也没有说明整个 json 字符串的字符编码。一些澄清将不胜感激。也许对于 Java 程序员来说,“unicode”这个词足以敲响警钟,但对于拥有 std::string 等的 C/C++ 程序员来说,这还不够。
@BitTickler unicode 字符一点也不含糊——它只是意味着它在 unicode 规范中有一个(或多个)代码点。当你使用 std::string 时,它是一堆 unicode 字符。当您需要序列化它时,让我们说一个文件或通过网络,这就是“哪种编码”的来源。根据 Thanatos 的说法,他们似乎希望您使用 UTF,但从技术上讲,任何编码都可以使用,只要它可以重构为 unicode 字符。
M
MonoThreaded

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

嗯,这是 OP 标签
不明白只有当c < ' ',改成\u。就我而言,有字符 \uD38D,即 55357 及以上 '',所以不会更改为 \u...
@Stony 听起来像是一个新问题
@MonoThreaded 感谢您的回复,我仍然不知道为什么。但最后,我改变了方法来修复它,如下所示, if (c < ' ' || c > 0x7f) { t = "000" + Integer.toHexString(c).toUpperCase(); sb.append("\\u" + t.substring(t.length() - 4)); } 其他 { sb.append(c); } }
@Stony,只要输出编码匹配,除 "\ 和控制字符(“ ”之前的那些)之外的所有字符在 JSON 字符串中都是有效的。换句话说,只要保留 UTF 编码,您就不需要将“펍”编码为 \uD38D
S
Stephan

试试这个org.codehaus.jettison.json.JSONObject.quote("your string")

在此处下载:http://mvnrepository.com/artifact/org.codehaus.jettison/jettison


绝对是最好的解决方案!谢谢
但这并没有引用 [{
@Sergei您不必在JSON字符串中转义大括号。
可能有助于显示实际返回的内容。
org.json.JSONObject.quote("your json string") 也可以正常工作
D
Dan-Dev

org.json.simple.JSONObject.escape() 转义引号、\、/、\r、\n、\b、\f、\t 和其他控制字符。它可用于转义 JavaScript 代码。

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

这取决于您使用的 json 库(JSONObject.escape、JSONObject.quote、..),但它始终是执行引用工作的静态方法,应该重复使用
org.json 属于哪个库?我的类路径中没有它。
d
dutoitns

Apache Commons Text 库中现在有一个 StringEscapeUtils#escapeJson(String) 方法。

感兴趣的方法如下:

StringEscapeUtils#escapeJson(String)

StringEscapeUtils#unescapeJson(字符串)

此功能最初是作为 Apache Commons Lang 3.2 版的一部分发布的,但后来被弃用并移至 Apache Commons Text。因此,如果该方法在您的 IDE 中被标记为已弃用,则说明您从错误的库中导入了实现(两个库都使用相同的类名:StringEscapeUtils)。

实现不是纯 Json。根据 Javadoc

使用 Json 字符串规则转义字符串中的字符。将它找到的任何值转义为它们的 Json 字符串形式。正确处理引号和控制字符(制表符、反斜杠、cr、ff 等)因此制表符变为字符“\”和“t”。 Java 字符串和 Json 字符串的唯一区别在于,在 Json 中,正斜杠 (/) 被转义了。有关详细信息,请参阅 http://www.ietf.org/rfc/rfc4627.txt。


这对我来说是最实际的答案。大多数项目已经使用 apache commons lang,因此无需为一个函数添加依赖项。 JSON builder 可能是最好的答案。
作为后续,由于我不知道如何编辑评论,我添加了一个新评论,我发现了 javax.json.JsonObjectBuilder 和 javax.json.JsonWriter。非常好的建造者/作家组合。
这在 apache commons lang 中已弃用,您需要使用 apache commons text。遗憾的是,这个库通过转义 / 字符来遵循可选/过时的规范。这会破坏很多东西,包括其中带有 URL 的 JSON。最初的提议将 / 作为特殊字符进行转义,但现在情况不再如此,正如我们在 the latest spec at time of writing 中看到的那样
@adamnfish 谢谢。我更新了答案。
我从此开始使用 Google 的 Gson 进行 Json 转换。与 Apache Commons 类似,他们的实现似乎有一些警告 [1] [2] 但我现在正在推进它......
I
I.G. Pascual

org.json.JSONObject quote(String data) 方法完成这项工作

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

从文档中提取:

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


org.apache.sling.commons.json.JSONObject 也有同样的事情
S
Syon

StringEscapeUtils.escapeJavaScript / StringEscapeUtils.escapeEcmaScript 也可以解决问题。


escapeJavaScript 将单引号转义为 \',这是不正确的。
D
Dhiraj

如果您使用的是 fastexml jackson,则可以使用以下内容:com.fasterxml.jackson.core.io.JsonStringEncoder.getInstance().quoteAsString(input)

如果您使用的是 codehaus jackson,则可以使用以下内容:org.codehaus.jackson.io.JsonStringEncoder.getInstance().quoteAsString(input)


V
Vladimir

不确定“手动创建 json”是什么意思,但您可以使用 gson (http://code.google.com/p/google-gson/) 之类的东西,这会将您的 HashMap、Array、String 等转换为 JSON 值。我建议为此使用一个框架。


手动我的意思不是使用像 Simple JSON、Gson 或 XStream 这样的 JSON 库。
只是好奇——你为什么不想使用这些 API 之一呢?这就像尝试手动转义 URL,而不是使用 URLEncode/Decode...
不太一样,这些库提供的不仅仅是 URLEncode/Decode 的等价物,它们包含一个完整的序列化包,允许以 json 形式持久化 java 对象,有时你真的只需要编码一小段文本
如果您不希望包含仅用于序列化少量数据的库,则手动创建 JSON 是有意义的
如果他们敢于在存在高质量库的情况下手动创建 JSON,我会要求从我参与的任何项目中删除团队成员。
E
EdChum

我没有花时间做出 100% 的确定,但它对我的输入很有效,足以被在线 JSON 验证器接受:

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

虽然它看起来并不比 org.codehaus.jettison.json.JSONObject.quote("your string")

我只是在我的项目中使用了速度工具——我的“手动 JSON”构建在速度模板中


v
vijucat

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


J
J28

在 commons lang API 中使用 EscapeUtils 类。

EscapeUtils.escapeJavaScript("Your JSON string");

请注意,例如,在转义为 javascript 或 json 时,单引号的处理方式不同。在 commons.lang 3.4 中,StringEscapeUtils (commons.apache.org/proper/commons-lang/javadocs/api-3.4/org/…) 有一个 escapeJSON 方法,它与 commons.lang 2 中的 escapeJavaScript 方法不同:commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/…
o
orip

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

w
webjockey

如果您需要在 JSON 字符串中转义 JSON,请使用 org.json.JSONObject.quote("your json string that need to be escape") 似乎效果很好


M
Mohsen

Apache commons-text 现在有一个 StringEscapeUtils.escapeJson(String)


D
David

使用 \uXXXX 语法可以解决这个问题,google UTF-16 加上符号的名字,可以查到 XXXX,例如:utf-16 双引号


S
Stefan Steiger

这里显示实际实现的方法都是错误的。我没有 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()方法如何出错?
a
absmiths

我认为 2017 年最好的答案是使用 javax.json API。使用 javax.json.JsonBuilderFactory 创建您的 json 对象,然后使用 javax.json.JsonWriterFactory 写出对象。非常好的建造者/作家组合。