6

JSON is not a subset of JavaScript. I need my output to be 100% valid JavaScript; it will be evaluated as such -- i.e., JSON.stringify will not (always) work for my needs.

Is there a JavaScript stringifier for Node?

As a bonus, it would be nice if it could stringify objects.

mpen
  • 272,448
  • 266
  • 850
  • 1,236

2 Answers2

12

You can use JSON.stringify and afterwards replace the remaining U+2028 and U+2029 characters. As the article linked states, the characters can only occur in the strings, so we can safely replace them by their escaped versions without worrying about replacing characters where we should not be replacing them:

JSON.stringify('ro\u2028cks').replace(/\u2028/g,'\\u2028').replace(/\u2029/g,'\\u2029')
SvdB
  • 415
  • 3
  • 8
1

From the last paragraph in the article you linked:

The solution

Luckily, the solution is simple: If we look at the JSON specification we see that the only place where a U+2028 or U+2029 can occur is in a string. Therefore we can simply replace every U+2028 with \u2028 (the escape sequence) and U+2029 with \u2029 whenever we need to send out some JSONP.

It’s already been fixed in Rack::JSONP and I encourage all frameworks or libraries that send out JSONP to do the same. It’s a one-line patch in most languages and the result is still 100% valid JSON.

Community
  • 1
  • 1
djechlin
  • 59,258
  • 35
  • 162
  • 290
  • I read that bit, but I can't quite figure it out. `JSON.stringify('ro\u2028cks'.replace('\u2028','\\u2028').replace('\u2029','\\u2029'))` gives `"ro\\u2028cks"` -- i.e., it's double escaped now. I don't want to have to reimplement the entire `JSON.stringify` method to accomplish this. – mpen Apr 14 '13 at 22:30
  • @Mark Me neither - but you can post a much more specific question regarding unicode escaping that anyone who knows JS can help you with, instead of something only node developers may have expertise in. – djechlin Apr 14 '13 at 22:33
  • @Mark note I would do this as a separate question; they are distinct enough and there may be a better answer to this one than what I have posted (ideally simply a feature in the JSON parser that encapsulates this.) – djechlin Apr 14 '13 at 22:34
  • Hrm? Escaping those two characters is easy; escaping everything that could possibly go into a JS string (not just unicode characters, but `\n` and whatever else) is a lot more complicated. I was hoping for a mature/tested library that works with Node. – mpen Apr 14 '13 at 22:39
  • 1
    @Mark You'd need to turn it into a JSON string first, and *then* replace the unicode characters: `JSON.stringify('ro\u2028cks').replace('\u2028','\\u2028').replace('\u2029','\\u2029')` – SvdB Apr 14 '13 at 22:48
  • @SvdB: I was thinking about that, but I thought it might have unintended consequences. Maybe I'm messing with something `JSON.stringify` already escaped? – mpen Apr 14 '13 at 22:56
  • @Mark: That's why the article said "If we look at the JSON specification we see that the only place where a U+2028 or U+2029 can occur is in a string.". So in the output of `JSON.stringify`, U+2028 and U+2029 will only occur in the string where you want to replace them. Note that the string '\u2029' which you are replacing will be a string consistent of just the U+2029 character; there are no backslashes in the actual string (just in the source code). – SvdB Apr 14 '13 at 23:03
  • @SvdB: Yeah...I hadn't thought it through yet :-) Anyway, you're welcome to post that line as an answer. I'll accept it. – mpen Apr 14 '13 at 23:09
  • 1
    @Mark: actually, that replace is insufficient; it will only replace the first occurrence of each character. Using a regexp with the global flag would do the trick: `JSON.stringify('ro\u2028cks').replace(/\u2028/g,'\\u2028').replace(/\u2029/g,'\\u2029')` – SvdB Apr 14 '13 at 23:27