Seemingly odd JSON.parse behaviour in node with double backslashes - javascript

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')
}

Related

How do I use newline characters in an interpolated string with newlines?

I am having issues adding newline characters into a string that already has newlines.
For example:
const foo = "test\ntest";
const string = `mutation {
some_field: "${foo}"
}`;
This will be output as:
mutation {
some_field: "test
test"
}
But I want it to output as:
mutation {
some_field: "test\ntest"
}
Where the existing white-space/newlines are preserved but you can add a string like "test\ntest" inside of it. This current line-breaking is causing syntax errors.
I have tried adding together these strings in various different ways but I can't figure out how to force \n to remain \n in the field value.
When you have \n in a JavaScript string literal, it represents a newline character, not a backslash followed by n. If you want a backslash followed by n, you have to escape the backslash character with another backslash character. That is, you have to put \n in the string literal.
const foo = "test\\ntest";
const string = `mutation {
some_field: "${foo}"
}`;
console.log(string)
You need to put literal \n in the string by escaping the backslash.
const foo = "test\\ntest";
const string = `mutation {
some_field: "${foo}"
}`;
console.log(string);
But this seems like it's fraught with danger. You should almost certainly be using JSON, not a template literal.
const foo = "test\ntest";
const string = `mutation ${JSON.stringify({
some_field: foo
}, null, 4)}`;
console.log(string);

Parse command string with nested quotes into arguments and flags

I'm trying to create a command parser for a Discord bot for when it receives a message, but I am having issues with dealing with nested quotes. I have made it so that it can parse a string with double quotes and flags, but it does not handle nested quotes.
Here are my requirements:
Handle double quotes.
Handle nested double quotes.
Handle flags (can be anywhere after !command).
A flag without a specified value defaults to a value of true/1.
For example, the following string:
!command that --can "handle double" quotes "and \"nested double\" quotes" --as --well=as --flags="with values"
...should result in the following arguments: command, that, handle double, quotes, and "nested double" quotes and the following flags: "can": true, "as": true, "well": "as", "flags": "with values".
Here is what I have so far:
// splits up the string into separate arguments and flags
const parts = content.slice(1).trim().match(/(--\w+=)?"[^"]*"|[^ "]+/g)
.map(arg => arg.replace(/^"(.*)"$/, '$1'));
// separates the arguments and flags
const [ args, flags ] = parts.reduce((parts, part) => {
// check if flag or argument
if (part.startsWith('--')) {
// check if has a specified value or not
if (part.includes('=')) {
// parses the specified value
part = part.split('=');
const value = part.slice(1)[0];
parts[1][part[0].slice(2)] = value.replace(/^"(.*)"$/, '$1');
} else {
parts[1][part.slice(2)] = true;
}
} else {
parts[0].push(part);
}
return parts;
}, [[], {}]);
This currently parses into the following arguments: command, that, handle double, quotes, and \, nested, double\, quotes and the following flags: "can": true, "as": true, "well": "as", "flags": "with values".
I modified the first RegEx to allow \" in the middle of quoted values. The following line:
const parts = content.slice(1).trim().match(/(--\w+=)?"[^"]*"|[^ "]+/g)
...changed to:
const parts = content.slice(1).trim().match(/(--\S+=)?"(\\"|[^"])*"|[^ "]+/g)
Modifications
The "[^"]*" section was changed to "(\\"|[^"])*" to allow \" to validate, preventing the quoted value from being terminated by quotes with backslashes before them.
I changed the \w in (--\w+=)? to a \S resulting in (--\S+=)? to allow more letters to validate.

Not accepting regex in IOS [duplicate]

I try to include ' symbol to Regular Expressions
I use this function
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: text,
range: NSRange(text.startIndex..., in: text))
return results.map {
text.substring(with: Range($0.range, in: text)!)
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
and this Regular Expressions
let matched = matches(for: "^[‘]|[0-9]|[a-zA-Z]+$", in: string)
when I search I can find numbers and english letters
but not ' symbol
I guess that what you really want is this:
"['0-9a-zA-Z]+"
Note that I have removed the ^ (text start) and $ (text end) characters because then your whole text would have to match.
I have merged the groups because otherwise you would not match the text as a whole word. You would get separate apostrophe and then the word.
I have changed the ‘ character into the proper ' character. The automatic conversion from the simple apostrophe is caused by iOS 11 Smart Punctuation. You can turn it off on an input using:
input.smartQuotesType = .no
See https://developer.apple.com/documentation/uikit/uitextinputtraits/2865931-smartquotestype

Escaped backslash doubled in object

I have a string with a backslash in it: const string = '/\\.txt/ which return the expected result (/\.txt/) but if I put this string to an object the escapes backslash is just duplicate itself, so my object returns this: { string: '/\\.txt/' }, even if I declare it with { string } or {string: string}.
So, have someone met with this issue before?
And, if yes, how can I fix this?
I have reproduced it this way:
var foo = {
bar: () => { const string = '/\\.' + 'alma'; return {string}}
};
Now
JSON.stringify(foo.bar())
will yield
"{"string":"/\.alma"}"
but if I negate the stringify with
JSON.parse(JSON.stringify(foo.bar()))
then the result will be
{string: "/.alma"}
so indeed, you are just seeing that the backslash needs to be escaped in JSON.

How to concatenate JavaScript variables into a complexe string with quotes and doubles quotes?

var id_person_value = 4;
$('#message').load("{{ path('MyBundle_ajax_modal_vote', {'id_person' :"+id_person_value+"}) }}", function() {//..
It should return
$('#message').load("{{ path('MyBundle_ajax_modal_vote', {'id_person' : 4 }) }}", function() {//..
But this returns me : http://mywebsite.com/app_dev.php/+id_person_value+
How could I inject the value correctly?
Thanks a lot for your help!
You can make use of escape characters such as \' to insert single quote inside the string and \" for double quotes
Instead of concatenating things together, which is tedious and error prone, try a formatting function, similar to printf or format in other languages:
str = format("{{ path('MyBundle_ajax_modal_vote', {'id_person' : {0} }) }}", 4)
Unfortunately, there's no such builtin in Javascript, but it's quite easy to write:
function format(str) {
var args = [].slice.call(arguments, 1);
return str.replace(/{(\d+)}/g, function($0, $1) {
return args[$1]
})
}
See JavaScript equivalent of Python's format() function? for more implementations.
use simple double quotes to start and end of string "". If you want to include any quote or double quote in your string use escape character \ before quote
eg you want string as s= hello"world
then s= "hello\""+"World" or s="hello"+"\"" + "world"

Categories