手动创建 JSON 数据时,我应该如何转义字符串字段?我应该使用 Apache Commons Lang 的 StringEscapeUtilities.escapeHtml
、StringEscapeUtilities.escapeXml
之类的东西,还是应该使用 java.net.URLEncoder
?
问题是当我使用 SEU.escapeHtml
时,它不会转义引号,并且当我将整个字符串包装在一对 '
中时,将生成格式错误的 JSON。
StringEscapeUtilities
大纲 +1。它非常有用。
理想情况下,在您的语言中找到一个 JSON 库,您可以向其中提供一些适当的数据结构,并让它担心如何转义。它会让你更清醒。如果出于某种原因,您的语言没有库,您不想使用(我不建议这样做¹),或者您正在编写 JSON 库,请继续阅读。
根据 RFC 对其进行转义。 JSON 非常自由:您必须转义的唯一字符是 \
、"
和控制代码(小于 U+0020 的任何字符)。
这种转义结构是 JSON 特有的。你需要一个 JSON 特定的函数。所有转义都可以写成 \uXXXX
,其中 XXXX
是该字符的 UTF-16 代码单元¹。有一些快捷方式,例如 \\
,也可以使用。 (它们会产生更小更清晰的输出。)
有关完整的详细信息,请参阅 the RFC。
¹JSON 的转义是建立在 JS 之上的,所以它使用 \uXXXX
,其中 XXXX
是一个 UTF-16 代码单元。对于 BMP 之外的代码点,这意味着编码代理对,这可能会有点麻烦。 (或者,您可以直接输出字符,因为 JSON 编码为 Unicode 文本,并允许这些特定字符。)
从 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();
}
"
、\
和控制字符(“ ”之前的那些)之外的所有字符在 JSON 字符串中都是有效的。换句话说,只要保留 UTF 编码,您就不需要将“펍”编码为 \uD38D
。
试试这个org.codehaus.jettison.json.JSONObject.quote("your string")
。
在此处下载:http://mvnrepository.com/artifact/org.codehaus.jettison/jettison
org.json.simple.JSONObject.escape() 转义引号、\、/、\r、\n、\b、\f、\t 和其他控制字符。它可用于转义 JavaScript 代码。
import org.json.simple.JSONObject;
String test = JSONObject.escape("your string");
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。
/
字符来遵循可选/过时的规范。这会破坏很多东西,包括其中带有 URL 的 JSON。最初的提议将 /
作为特殊字符进行转义,但现在情况不再如此,正如我们在 the latest spec at time of writing 中看到的那样
org.json.JSONObject
quote(String data)
方法完成这项工作
import org.json.JSONObject;
String jsonEncodedString = JSONObject.quote(data);
从文档中提取:
将数据编码为 JSON 字符串。这将应用引号和任何必要的字符转义。 [...] Null 将被解释为空字符串
org.apache.sling.commons.json.JSONObject
也有同样的事情
StringEscapeUtils.escapeJavaScript
/ StringEscapeUtils.escapeEcmaScript
也可以解决问题。
escapeJavaScript
将单引号转义为 \'
,这是不正确的。
如果您使用的是 fastexml jackson,则可以使用以下内容:com.fasterxml.jackson.core.io.JsonStringEncoder.getInstance().quoteAsString(input)
如果您使用的是 codehaus jackson,则可以使用以下内容:org.codehaus.jackson.io.JsonStringEncoder.getInstance().quoteAsString(input)
不确定“手动创建 json”是什么意思,但您可以使用 gson (http://code.google.com/p/google-gson/) 之类的东西,这会将您的 HashMap、Array、String 等转换为 JSON 值。我建议为此使用一个框架。
我没有花时间做出 100% 的确定,但它对我的输入很有效,足以被在线 JSON 验证器接受:
org.apache.velocity.tools.generic.EscapeTool.EscapeTool().java("input")
虽然它看起来并不比 org.codehaus.jettison.json.JSONObject.quote("your string")
好
我只是在我的项目中使用了速度工具——我的“手动 JSON”构建在速度模板中
对于像我这样来这里寻找命令行解决方案的人来说,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 的教程 :-)
在 commons lang API 中使用 EscapeUtils 类。
EscapeUtils.escapeJavaScript("Your JSON string");
考虑 Moshi 的 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();
如果您需要在 JSON 字符串中转义 JSON,请使用 org.json.JSONObject.quote("your json string that need to be escape") 似乎效果很好
使用 \uXXXX 语法可以解决这个问题,google UTF-16 加上符号的名字,可以查到 XXXX,例如:utf-16 双引号
这里显示实际实现的方法都是错误的。我没有 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()
方法如何出错?
我认为 2017 年最好的答案是使用 javax.json API。使用 javax.json.JsonBuilderFactory 创建您的 json 对象,然后使用 javax.json.JsonWriterFactory 写出对象。非常好的建造者/作家组合。
{[]}:?
不能用单个反斜杠转义。 (例如,\:
在 JSON 字符串中无效。)所有这些都可以使用\uXXXX
语法进行转义,但会浪费几个字节。请参阅 RFC 的第 2.5 节。JSON.stringify()
可以完成这项工作。