4

I am running into an odd little problem with parsing some JSON which has quotes in it. I am using the native JSON.stringify and JSON.parse functions to do this. If I stringify an object which an object which has quote marks in it, they are escaped as one would expect. If I then parse this back into an object, it again works fine.

The problem is occurring where I stringify, then print the object to a page, then parse the resulting string. If I try to do this, the parse function fails as stringify has only put single slashes before each of the offending quote marks.

The reason I need to achieve this is I am working on an application that dynamically loads content stored in a database as JSON strings. The strings at some point need to be printed onto the page somewhere so that the javascript can find them and build the page based on their contents. I need some way of robustly passing the object into and out of strings which will not fail if a user inputs the wrong characters!

I can solve this for the moment by inserting extra slashes into the code with a replace call, but I was wondering if there is a better way to handle this?

I have put together a couple of jsfiddles to illustrate what I am trying to describe:

http://jsfiddle.net/qwUAJ/ (Stringify then parse back)

var ob = {};
ob["number1"] = 'Number "1"';
ob["number2"] = 'Number 2';
ob["number3"] = 'Number 3';

var string = JSON.stringify(ob);
var reOb = JSON.parse('{"number1":"Number \"1\"","number2":"Number 2","number3":"Number 3"}');

$('div').html(string);

http://jsfiddle.net/a3gBf/4/ (Stringify, then print, then parse back)

// Make an object
var ob = {};
ob["number1"] = 'Number "1"';
ob["number2"] = 'Number 2';
ob["number3"] = 'Number 3';

// Turn the object into a JSON string
var string = JSON.stringify(ob);

// Printing the string outputs
// {"number1":"Number \"1\"","number2":"Number 2","number3":"Number 3"}
$('.stringified').html(string);

// Attempt to turn the printed string back into an object
var reOb = JSON.parse('{"number1":"Number \"1\"","number2":"Number 2","number3":"Number 3"}');

// This fails due to the single escaped quote marks.

Thank you for any help in advance!

Gruffy
  • 1,431
  • 1
  • 14
  • 18
  • I think `replace()` is your only option given the output from `JSON.stringify`. – Rory McCrossan Aug 16 '13 at 12:55
  • 1
    You are copying the text from the console output into your code. Why would you do that instead of using the `string` variable? As far as I know, the output in the browser is displayed in which ever way the browser sees fit and may not even be valid in code (not 100% on that though). For example when you do `JSON.parse(string);`, which works, then do `console.log(reOb)` you can also see the new output is: `Object {number1: "Number "1"", number2: "Number 2", number3: "Number 3"}` which would not even parse in code due to the repeat quotes in `"Number "1""`. – Nope Aug 16 '13 at 12:57
  • 1
    I don't understand the issue. Why are you manually typing the string a second time? http://jsfiddle.net/a3gBf/3/ – Explosion Pills Aug 16 '13 at 12:59
  • 1
    Just to add, the question is a little confusing. You are not manually creating a string in your examples but start from a JavaScript object, hence the question is odd as you can simply continue using the variables. If you how ever would receive a string automatically or manually generated from somewhere else and it fails to parse then you need to make sure what ever generates your string generates a valid format. – Nope Aug 16 '13 at 13:02
  • In the app I am working on, the JSON string is taken from a mySQL database, then printed to the page via PHP. This is then picked up by the javascript on the page and processed. As far as I can see this prevents me from using the string itself as at some point it will have to be printed to the page in order to make the jump from PHP string to JS object? – Gruffy Aug 16 '13 at 13:03

2 Answers2

7

This is a problem which arises from re-evaluating a String without first converting it back into a string literal, so the meaning changes if it is even still valid.
You need to consider what does '\"' as a literal actually mean? The answer is ", without the \. Why?

  • \" resolves to "

If you want to have \" as the result of the literal, you need to write '\\\"'

  • \\ resolves to \
  • \" resolves to "

So basically, the extra slashes are required to escape any characters with special meaning in string literals.

If you did var reOb = JSON.parse($('.stringified').html()); it would work fine as is.


Consider further

str = '\\\"\\\'';      //      \"\'
str = '\"\'';          //      "'
str = '"'';            // SyntaxError: Unexpected token ILLEGAL

As far as I'm aware, JavaScript offers no native implementation to convert strings as desired, so the easiest method I know of is using a replace

function toLiteral(str) {
    var dict = {'\b': 'b', '\t': 't', '\n': 'n', '\v': 'v', '\f': 'f', '\r': 'r'};
    return str.replace(/([\\'"\b\t\n\v\f\r])/g, function ($0, $1) {
        return '\\' + (dict[$1] || $1);
    });
}
toLiteral('foo\\bar'); // "foo\\bar"
Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • Hi Paul, I realise that they are required and understand what they do, I guess I am asking if there is a way stringify to output a pre-escaped string, or at least a string I do not have to re-process somehow with replace() or the like before it can be parsed? – Gruffy Aug 16 '13 at 12:59
  • 1
    The _String_ in memory does not need any changes to work fine in `JSON.parse`, it's the resulting string which you didn't turn back into a _String_ literal before re-evaluating it which needs any backslashes, quotes, new lines etc escaped to be turned back into a literal. – Paul S. Aug 16 '13 at 13:02
  • @Gruffy You save the string in a DB if you fetch it later with getJSON or $.get then you just serve the JSON as JS has stringified it. If you generate JS code with your PHP then it gets more complicated. – HMR Aug 16 '13 at 13:04
  • @PaulS. So is there a way to turn it back into a string literal (dynamically) without iterating over it with a replace() call to stick back the slashes required? – Gruffy Aug 16 '13 at 13:11
  • @Gruffy updated answer to include two ways to make it work fine; one being take the value of the `html` directly, the other a function to create a literal (which yes, relies on `replace`). – Paul S. Aug 16 '13 at 13:25
  • @PaulS. Cracking, you are a gentleman and a scholar ;) Taking it via .html() is exactly what I was looking for - it seems more robust than relying on straight up replacement. It will require rewriting some of the app as it was given to me but I think this will be for the best. – Gruffy Aug 16 '13 at 13:41
2

If you generate JS with PHP code you should escape the quotes in your JSON string:

//PHP code generating js code
echo "var myJSONString = \"". str_replace("\"","\\\"",$mySqlJSON)."\";";
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Thank you for the PHP based solution but I am looking to do this via JS to fit in with the way that the app is built / has been handed to me! – Gruffy Aug 16 '13 at 13:42
  • @Gruffy How does it fit? You've mentioned saving the JSON string in the database but haven't mentioned how to serve it. Escaping the JSON string in this way has to be done if you serve the saved JSON as JS or won't generate valid JS (you'll get a syntax error) as mentioned by Paul. – HMR Aug 16 '13 at 13:59
  • 1
    @Gruffy If at a later time you descide to serve the JSON as JSON and request it from your pages using $.get or $.getJSOn you're in trouble because your db is filled with invalid JSON (you've made it invalid by the JS replace method that Paul offerred). – HMR Aug 16 '13 at 14:04