5

I'm writing a service in nodejs that replaces an existing system written in .NET. The service provides a JSON API, one of the calls returns a date. The Microsoft date format for JSON was/is where 1599890827000 is the milliseconds offset:

/Date(1599890827000)/

The problem I am having is that JSON.stringify (used in res.send and res.json in express) does not escape forward slashes but the existing Microsoft library (System.Web.Script.Serialization.JavaScriptSerializer) expects forward slashes to be escaped.

For example, the client expects JSON like this:

{
  "Expires": "\/Date(1599890827000)\/"
}

But JSON.stringify produces the following:

{
  "Expires": "/Date(1599890827000)/"
}

The second result is perfectly valid but the Microsoft library doesn't like it and fails to parse.

Is there any way I can force Express/Node/JSON to escape forward slashes in JSON.stringify or handle this case?

I could use a regex replacement after running stringify but because of an object caching system we use in the project it would be very hacky to have to convert to JSON before sending to the client instead of letting.

Note: I cannot change the client, only the api service.

antfx
  • 3,205
  • 3
  • 35
  • 45
  • 2
    *"but the existing Microsoft library (System.Web.Script.Serialization.JavaScriptSerializer) expects forward slashes to be escaped"* Not in my experience. That would be seriously weird, as there's exactly no reason to escape them either in JSON or in JavaScript. I suspect your problem is elsewhere. – T.J. Crowder Mar 07 '16 at 10:43
  • 1
    Strange indeed but without the escaped forward slash I get this exception `System.FormatException: /Date(1599898027000)/ is not a valid value for DateTime.` and I cannot change the client – antfx Mar 07 '16 at 10:46
  • Better late than never, but I've got an explanation for the behavior. [This blog post from 2008](https://weblogs.asp.net/bleroy/dates-and-json) outlines the .NET team's decision here; basically, they needed a way to differentiate dates from strings, and decided to intentionally use escaped forward slashes for that purpose since "there actually is no reason that I can think of why you'd want to do that". Personal opinion: definitely not the best decision to come out of Redmond. But it's one that's made it into the wild regardless. – Josh Doebbert Jun 18 '21 at 15:41

2 Answers2

6

Replacer happens before escaping, leaving you with either:

"/Date(1599890827000)/"

Or:

"\\/Date(1599890827000)\\/"

Realistically you will have to run a string replace on the resulting output:

JSON.stringify(data).replace(/\//g, '\\/');

This means that you won't be able to use the express built in res.json(data) and may need to instead write a function to replace it like:

function dotNetJSONResponse(res, data) {

    var app = res.app;
    var replacer = app.get('json replacer');
    var spaces = app.get('json spaces');
    var body = JSON.stringify(data, replacer, spaces);

    if (!this.get('Content-Type')) {
        res.set('Content-Type', 'application/json');
    }

    return res.send(body.replace(/\//g, '\\/'));
}

Calling it as:

app.get('/url', function (req, res) {
     dotNetJSONResponse(res, data);
});

However, that said, fixing the behaviour in .NET would be the more forward compatible solution.

Stuart Wakefield
  • 6,294
  • 24
  • 35
3

Use replacer parameter

function replaceSlashes(key, value) 
{
  if ( typeof value == "string")
  {
    value = value.replace(/\//g, "\\/");
  }
  return value;
}

var jsonString = JSON.stringify(jsonString, replaceSlashes);

It will still produce a double backward slash since stringify method by itself won't product a single slash without escaping the same.\

You need to try something like this

JSON.stringify({
  "Expires": "\/Date(1599890827000)\/"
}, replaceSlashes).replace(/\\\\/g, "\\");
gurvinder372
  • 66,980
  • 10
  • 72
  • 94
  • @T.J.Crowder Please check now, it is escaping now – gurvinder372 Mar 07 '16 at 10:43
  • Thanks but this produces `\\/Date(1599890827000)\\/` instead of `\/Date(1599890827000)\/`I guess because stringify is then escaping the new backslash – antfx Mar 07 '16 at 10:55
  • @antfx It won't be possible directly with stringify method. You will need to try something like `JSON.stringify({ "Expires": "\/Date(1599890827000)\/" }, replaceSlashes).replace(/\\\\/g, "\\")` Check the updates – gurvinder372 Mar 07 '16 at 11:11
  • Yeah it works but as I mentioned in my original post, I ideally wanted something to set either globally or in Express because of an object caching system we use – antfx Mar 07 '16 at 12:03
  • @antfx I am not sure if I have understood `either globally or in Express` . Sorry, I have not worked on Express.js, if that is what you meant. – gurvinder372 Mar 07 '16 at 12:05
  • Beware that this code may have unexpected results with other items in the JSON object: https://jsfiddle.net/5c48vr6r/ – Stuart Wakefield Mar 07 '16 at 13:04