Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Seemingly odd JSON.parse behaviour in node with double backslashes

A colleague was attempting to parse a json string generated in another system and ran into behavior we were not able to explain. I've replicated the problem with a very small code example here:

// running in node >= 8 (tried both 8 and 12)

const str1 = '{ "test": "path\\test" }';
const str2 = '{ "test": "path\\atest" }';

try {
  console.log('str1:', JSON.parse(str1));
} catch (e) {
  console.log('str1 fail')
}

try {
  console.log('str2:', JSON.parse(str2))
} catch (e) {
  console.log('str2 fail')
}

str1 will successfully parse into the desired format of { test: 'path\test' }
str2 will fail.

It seems like this will only succeed if the character immediately following \\ is one of the valid JavaScript escape characters

(\t: horizontal tab in the str1 case)

The intended behavior is to escape the \\ into a single \

Is there an explanation for this behavior? We're stumped and would appreciate any insight!

like image 362
Trevor Atlas Avatar asked Dec 05 '25 15:12

Trevor Atlas


1 Answers

When you have a string literal with two backslashes, those two backslashes will be interpreted as a single literal backslash, Eg, the .length of '\\test' is 5, not 6.

JSON.parse only allows backslashes before characters that can be escaped, like t (horizontal tab) and n (newline). When you have a literal backslash before a character that can't be escaped (like a), JSON.parse throws an error. (This is unlike Javascript string literals, which can have unnecessarily escaped normal characters - eg const str = '\a' is equivalent to 'a', and doesn't throw an error.) You can see an illustration of what's permitted in JSON here - as you can see by the fourth graphic, after a \, the only permitted characters are one of "\/bfnrt, or uXXXX, where each X is a hex digit.

If you wanted the value of the test property in the parsed object to be the string path, followed by a literal backslash, followed by test or atest, you'd need to use four backslashes when declaring the string literal - first, to have the Javascript interpreter interpret it as two literal backslashes, and second, to have JSON.parse interpret the two literal backslashes as a single backslash in the parsed string.

const str1 = '{ "test": "path\\\\test" }';
const str2 = '{ "test": "path\\\\atest" }';

try {
    console.log('str1:', JSON.parse(str1));
} catch (e) {
    console.log('str1 fail')
}

try {
    console.log('str2', JSON.parse(str2))
} catch (e) {
    console.log('str2 fail')
}

You can also define string literals with String.raw for any single backslash in the string to be interpreted as a single literal backslash (rather than the beginning of an escape sequence):

const str1 = String.raw`{ "test": "path\\test" }`;
const str2 = String.raw`{ "test": "path\\atest" }`;

try {
    console.log('str1:', JSON.parse(str1));
} catch (e) {
    console.log('str1 fail')
}

try {
    console.log('str2', JSON.parse(str2))
} catch (e) {
    console.log('str2 fail')
}
like image 116
CertainPerformance Avatar answered Dec 07 '25 05:12

CertainPerformance



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!