Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force JSON-lib's JSONObject.put(..) to escape a string containing JSON?

When using JSON-lib's JSONObject, how can I stop the put method from storing a String which contains JSON as JSON rather than as an escaped string?

For instance:

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

prints:

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

and I want it to print:

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

Yes, I realize this is obnoxious. However, this is in support of a JSON serialization pipeline for which, for interoperability's sake, this is the expected behavior. There are cases in which we would be serializing user input which may be/contain valid JSON. We wouldn't want the user input to become a part of the JSON object that we're serializing said input to.

Manual escaping doesn't work because it causes JSON-lib to escape the \ characters:

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

Output:

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

At this point, any workarounds to enable manual selective escaping of a complex JSON object would completely negate the value of using JSON-lib in the first place.

Also, I understand that this question has been asked before, but unfortunately I cannot accept its answer so easily. JSON-lib is a heavily-used dependency in many areas of my project and swapping it out would be a big undertaking. I need to be absolutely sure that there's no way to achieve this goal with JSON-lib before I can entertain a swap to Jackson, simple-json, or Gson.

like image 908
Ben Burns Avatar asked May 27 '11 17:05

Ben Burns


People also ask

How do you escape a JSON string?

JSON is pretty liberal: The only characters you must escape are \ , " , and control codes (anything less than U+0020). This structure of escaping is specific to JSON. You'll need a JSON specific function. All of the escapes can be written as \uXXXX where XXXX is the UTF-16 code unit¹ for that character.

How do you escape a JSON string in Java?

You can escape String in Java by putting a backslash in double quotes e.g. " can be escaped as \" if it occurs inside String itself. This is ok for a small JSON String but manually replacing each double quote with an escape character for even a medium-size JSON is time taking, boring, and error-prone.

How do you escape a colon in JSON?

You must surround the string value with double quotes. Save this answer.


2 Answers

This worked for me with json-lib 2.4:

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

The output is:

{"jsonStringValue":"{\"hello\":\"world\"}","naturalStringValue":"\"hello world\""}
like image 93
marsbear Avatar answered Nov 13 '22 21:11

marsbear


Use single quotes to quote the string. From the documentation:

Strings may be quoted with ' (single quote).

Strings do not need to be quoted at all if they do not begin with a quote or single quote, and if they do not contain leading or trailing spaces, and if they do not contain any of these characters: { } [ ] / \ : , = ; # and if they do not look like numbers and if they are not the reserved words true, false, or null.

So modifying your example:

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

Produces:

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

Note how quotedJsonStringValue has been treated as a string value and not JSON, and appears quoted in the output JSON.

like image 45
Paul V Avatar answered Nov 13 '22 20:11

Paul V