【问题标题】:How to force JSON-lib's JSONObject.put(..) to escape a string containing JSON?如何强制 JSON-lib 的 JSONObject.put(..) 转义包含 JSON 的字符串?
【发布时间】:2011-09-03 13:49:24
【问题描述】:

使用 JSON-lib 的 JSONObject 时,如何阻止 put 方法将包含 JSON 的字符串存储为 JSON 而不是转义字符串?

例如:

JSONObject obj = new JSONObject();
obj.put("jsonStringValue","{\"hello\":\"world\"}");
obj.put("naturalStringValue", "\"hello world\"");
System.out.println(obj.toString());
System.out.println(obj.getString("jsonStringValue"));
System.out.println(obj.getString("naturalStringValue"));

打印:

{"jsonStringValue":{"hello":"world"},"naturalStringValue":"\"hello world\""}
{"hello":"world"}
"hello world"

我希望它打印出来:

{"jsonStringValue":"{\"hello\":\"world\"}","naturalStringValue":"\"hello world\""}
{"hello":"world"}
"hello world"

是的,我知道这很讨厌。但是,这是为了支持 JSON 序列化管道,为了互操作性,这是预期的行为。在某些情况下,我们会序列化可能是/包含有效 JSON 的用户输入。我们不希望用户输入成为我们将所述输入序列化到的 JSON 对象的一部分。

手动转义不起作用,因为它会导致 JSON-lib 转义 \ 字符:

JSONObject obj = new JSONObject();
obj.put("naturalJSONValue","{\"hello\":\"world\"}");
obj.put("escapedJSONValue", "{\\\"hello\\\":\\\"world\\\"}");
System.out.println(obj.toString());
System.out.println(obj.getString("naturalJSONValue"));
System.out.println(obj.getString("escapedJSONValue"));

输出:

{"naturalJSONValue":{"hello":"world"},"escapedJSONValue":"{\\\"hello\\\":\\\"world\\\"}"}
{"hello":"world"}
{\"hello\":\"world\"}

此时,任何启用手动选择性转义复杂 JSON 对象的解决方法都会完全否定使用 JSON-lib 的价值。

另外,我知道有人问过这个问题before,但不幸的是我不能这么轻易地接受它的答案。 JSON-lib 是我项目的许多领域中大量使用的依赖项,将其换出将是一项艰巨的任务。在我可以考虑换成 Jackson、simple-json 或 Gson 之前,我需要绝对确定没有办法使用 JSON-lib 实现这一目标。

【问题讨论】:

    标签: java json serialization json-lib


    【解决方案1】:

    这对我来说适用于 json-lib 2.4:

    System.out.println(
        new JSONStringer()
            .object()
                .key("jsonStringValue")
                    .value("{\"hello\":\"world\"}")
                .key("naturalStringValue")
                    .value("\"hello world\"")
            .endObject()
        .toString());
    

    输出是:

    {"jsonStringValue":"{\"hello\":\"world\"}","naturalStringValue":"\"hello world\""}
    

    【讨论】:

    • 我会考虑它,但根据互操作性要求,此序列化管道必须与现有系统兼容,这是一种常见的用例。
    • 这是一个特定的现有遗留系统,必须与之交互。系统将不受信任的 JSON 作为正确转义的字符串传输,而不是作为对象本身的一部分。我需要复制这种行为。
    • 那么我之前的建议呢?但是在对你的东西进行 json 编码之后直接应用呢?方法:预先转义特殊字段,json-lib 对所有内容进行编码,最后对这些特殊字段进行后转义。但这是一项相当大的努力,但使用正则表达式并不难。
    • 根据问题“此时,任何启用手动选择性转义复杂 JSON 对象的解决方法都将完全否定首先使用 JSON-lib 的价值。” - 特别是如果所述手动转义发生在对象序列化之后。我宁愿在那个时候换掉图书馆。
    • 另外,使用正则表达式也不是那么容易实现的。为了安全起见,这需要有保证的解决方案,不会受到奇怪的边缘情况的影响,并且这种类型的转义只能在 JSON 对象中的特定字段上执行。
    【解决方案2】:

    使用单引号来引用字符串。来自documentation

    字符串可以用 ' (单引号)引用。

    如果字符串不以引号或单引号开头,并且不包含前导或尾随空格,并且不包含以下任何字符,则根本不需要引用: { } [ ] / \ : , = ; # 如果它们看起来不像数字,并且它们不是保留字 true、false 或 null。

    所以修改你的例子:

    net.sf.json.JSONObject obj = new net.sf.json.JSONObject();
    obj.put("jsonStringValue","{\"hello\":\"world\"}");
    obj.put("quotedJsonStringValue","\'{\"hello\":\"world\"}\'");
    obj.put("naturalStringValue", "\"hello world\"");
    System.out.println(obj.toString());
    System.out.println(obj.getString("jsonStringValue"));
    System.out.println(obj.getString("quotedJsonStringValue"));
    System.out.println(obj.getString("naturalStringValue"));
    

    生产:

    {"jsonStringValue":{"hello":"world"},"quotedJsonStringValue":"{\"hello\":\"world\"}","naturalStringValue":"\"hello world\""}
    {"hello":"world"}
    {"hello":"world"}
    "hello world"
    

    注意quotedJsonStringValue 如何被视为字符串值而非 JSON,并在输出 JSON 中出现引用。

    【讨论】:

    • 据我了解,字段内容是由用户创建的,可能包含任何内容,包括任何类型的引号和/或 json。这也适用于您的示例吗?
    • @marsbear,如果数据可以包含单引号,那么是的,它们需要被转义或剥离。使用object().key().value() 肯定会更容易,我只是插话,因为@Ben 似乎需要使用JSONObject 的解决方法。
    猜你喜欢
    • 1970-01-01
    • 2011-05-14
    • 1970-01-01
    • 2014-12-11
    • 1970-01-01
    • 2019-04-03
    • 1970-01-01
    相关资源
    最近更新 更多