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!
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')
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With