Parsing comma separated key value pair - javascript

I want to turn
realm="https://api.digitalocean.com/v2/registry/auth",service="registry.digitalocean.com",scope="registry:catalog:*"
To
{
realm: "https://api.digitalocean.com/v2/registry/auth",
service: "registry.digitalocean.com",
scope: "registry:catalog:*"
}
I don't know what does it call, but I think there should be easier to parse these type of format. I wonder if there is existing library or easier way to parse this one?
Currently, this is what I do, but I feel it is not reliable.
auth = `realm="https://api.digitalocean.com/v2/registry/auth",service="registry.digitalocean.com",scope="registry:catalog:*"`;
const convertToJsonString = '{"' + auth.replace(/="/g, `":"`).replace(/",/g, `","`) + '}';
JSON.parse(convertToJsonString);
What I have to achieve is to parse the Www-Authenticate header as the following spec:
https://www.rfc-editor.org/rfc/rfc2617#section-3.2.1
https://datatracker.ietf.org/doc/html/rfc7235#section-4.1
I am surprised that something that common has no existing library for parsing or maybe I don't know where to look for.

Assuming all entries are well-formed and ", within the values are escaped with backslashes (\",), then the following incantation does the trick:
Object.fromEntries(
s
.replace(/(?<!\\)",/g, '"\n') // replace separators by newlines
.split("\n") // split by newlines
.map((r) => /^(.+?)=(.+?)$/.exec(r)) // split by first equals
.map(([, k, v]) => [k, JSON.parse(v)]), // parse value as JSON string
)

Related

Best practice for converting string to object in JavaScript

I am working on a small UI for JSON editing which includes some object and string manipulation. I was able to make it work, but one of the fields is bit tricky and I would be grateful for an advice.
Initial string:
'localhost=3000,password=12345,ssl=True,isAdmin=False'
Should be converted to this:
{ app_server: 'localhost:3000', app_password:'12345', app_ssl: 'True', app_isAdmin: 'False' }
I was able to do that by first splitting the string with the ',' which returns an array. And then I would loop through the second array and split by '='. In the last step I would simply use forEach to loop through the array and create an object:
const obj = {}
arr2.forEach((item) => (obj[`app_${item[0]}`] = item[1]));
This approach works, but in case some of the fields, i.e password contains ',' or '=', my code will break. Any idea on how to approach this? Would some advanced regex be a good idea?
Edit: In order to make things simple, it seems that I have caused an opposite effect, so I apologize for that.
The mentioned string is a part of larger JSON file, it is the one of the values. On the high level, I am changing the shape of the object, every value that has the structure I described 'server='something, password=1234, ssl=True', has to be transformed into separate values which will populate the input fields. After that, user modify them or simply download the file (I have separate logic for joining the input fields into the initial shape again)
Observation/Limitation with the design that you have :
As per your comment, none of the special characters is escaped in any way then how we will read this string password=12345,ssl=True ? It will be app_password: 12345,ssl=True or app_password: 12345 ?
why localhost=3000 is converted into app_server: 'localhost:3000' instead of app_localhost: '3000' like other keys ? Is there any special requirement for this ?
You have to design your password field in the way that it will not accept at least , character which is basically used to split the string.
Here you go, If we can correct the above mentioned design observations :
const str = 'localhost=3000,password=123=45,ssl=True,isAdmin=False';
const splittedStr = str.split(',');
const result = {};
splittedStr.forEach(s => {
const [key, ...values] = s.split('=')
const value = values.join('=');
result[`app_${key}`] = value
});
console.log(result);
As you can see in above code snippet, I added password value as 123=45 and it is working properly as per the requirement.
You can use a regular expression that matches key and value in the key=value format, and will capture anything between single quotes when the value happens to start with a single quote:
(\w+)=(?:'((?:\\.|[^'])*)'|([^,]+))
This assumes that:
The key consists of alphanumerical characters and underscores only
There is no white space around the = (any space that follows it, is considered part of the value)
If the value starts with a single quote, it is considered a delimiter for the whole value, which will be terminated by another quote that must be followed by a comma, or must be the last character in the string.
If the value is not quoted, all characters up to the next comma or end of the string will be part of the value.
As you've explained that the first part does not follow the key=value pattern, but is just a value, we need to deal with this exception. I suggest prefixing the string with server=, so that now also that first part has the key=value pattern.
Furthermore, as this input is part of a value that occurs in JSON, it should be parsed as a JSON string (double quoted), in order to decode any escaped characters that might occur in it, like for instance \n (backslash followed by "n").
Since it was not clarified how quotes would be escaped when they occur in a quoted string, it remains undecided how for instance a password (or any text field) can include a quote. The above regex will require that if there is a character after a quote that is not a comma, the quote will be considered part of the value, as opposed to terminating the string. But this is just shifting the problem, as now it is impossible to encode the sequence ', in a quoted field. If ever this point is clarified, the regex can be adapted accordingly.
Implementation in JavaScript:
const regex = /(\w+)=(?:'(.*?)'(?![^,])|([^,]+))/g;
function parse(s) {
return Object.fromEntries(Array.from(JSON.parse('"server=' + s + '"').matchAll(regex),
([_, key, quoted, value]) => ["app_" + key, quoted ?? (isNaN(value) ? value : +value)]
));
}
// demo:
// Password includes here a single quote and a JSON encoded newline character
const s = "localhost:3000, password='12'\\n345', ssl='True', isAdmin='False'";
console.log(parse(s));

Parse Ascii Escaped String in JavaScript

Say we're given a string that itself contains ASCII escape characters, such as "\x64" (note that this is the contents of the string, not what it would be in code). Is there an easy way in JavaScript to parse this to its actual character?
There's the old trick of using JSON.parse but that seems to only work with unicode escape sequences and can't recognize ASCII ones, as in:
JSON.parse(`"\\u0064"`); // Output: "d" (as expected)
JSON.parse(`"\\x64"`); // ERROR: Unexpected token x
Is there an equivalent that would work for the ASCII escape sequence? I know that using eval() works but I'd really rather not use eval.
EDIT: To clarify, some characters in the original string may not be escaped; as we may need to parse "Hello Worl\x64", for instance.
One solution is to replace all the \x** patterns in the string, using a callback to parse them as base 16 integers and pass the result to String.fromCharCode to convert to a character:
const str = "\\x48\\x65ll\\x6f\\x20W\\x6f\\x72\\x6c\\x64";
const res = str.replace(/\\x(..)|./g, (m, p1) => p1 ? String.fromCharCode(parseInt(p1, 16)) : m);
console.log(res);
If you don't mind overwriting the original string, this can be simplified slightly to:
let str = "\\x48\\x65ll\\x6f\\x20W\\x6f\\x72\\x6c\\x64";
str = str.replace(/\\x(..)/g, (m, p1) => String.fromCharCode(parseInt(p1, 16)));
console.log(str);
You can use the eval function to evaluate a string in the same way it would be evaluated from Javascript source code, you'll only need to make sure you quote its content as such:
eval("\"\\x64\"") // will return the javascript string: "d"

Converting an array in curly braces to a square bracketed array

I've inherited a database that stores an array of strings in the following format:
{"First","Second","Third","Fourth"}
This is output as an ordered list in the application. We're replacing the front-end mobile app at the moment (ionic / angular) and want to do an ngFor over this array. In the first iteration, we did a quick and dirty replace on the curly brackets and then split the string on "," but would like to use a better method.
What is the best method for treating this type of string as an array?
You could do a string replacement of braces to brackets:
str.replace(/{(.*)}/, '[$1]')
This particular string could then be parsed as an array (via JSON.parse).
If you're looking to do the parsing to an array on the front end, would this work?:
const oldStyle = '{"First","Second","Third","Fourth"}'
const parseOldStyleToArray = input => input
.replace(/[\{\}]/g, '')
.split(',')
.map(item => item.replace(/\"/g, ''))
const result = parseOldStyleToArray(oldStyle)
console.dir(result)
Another way to do more widest replacement by key:value mapping.
str = '{"First","Second","Third","Fourth"}';
mapping = {
'{': '[',
'}': ']'
}
result = str.replace(/[{}]/g, m => mapping[m]);
console.log(result);

How to convert an array like string to array in node.js?

Actually I'm getting the arraylist from android device in node.js . But as it's in string form so I wanna convert it into an array . For that I've referred a lot of similar questions in SO but none of them were helpful . I also tried to use JSON.parse() but it was not helpful.
I'm getting societyList in form '[Art, Photography, Writing]'.Thus how to convert this format to an array?
Code:
var soc_arr=JSON.parse(data.societyList)
console.log(soc_arr.length)
use something like this
var array = arrayList.replace(/^\[|\]$/g, "").split(", ");
UPDATE:
After #drinchev suggestion regex used.
regex matches char starts with '[' and ends with ']'
This string is not valid JSON since it does not use the "" to indicate a string.
The best way would be to parse it yourself using a method like below:
let data = '[test1, test2, test3]';
let parts = data
.trim() // trim the initial data!
.substr(1,data.length-2) // remove the brackets from string
.split(',') // plit the string using the seperator ','
.map(e=>e.trim()) // trim the results to remove spaces at start and end
console.log(parts);
RegExp.match() maybe
console.log('[Art, Photography, Writing]'.match(/\w+/g))
So match() applies on any string and will split it into array elements.
Use replace and split. In addition, use trim() to remove the trailing and leading whitespaces from the array element.
var str = '[Art, Photography, Writing]';
var JSONData = str.replace('[','').replace(']','').split(',').map(x => x.trim());
console.log(JSONData);

Javascript Regular expression not working as expected

I have string which is in form of JSON but not a valid JSON string. String is like as below (Its single line string but I have added new lines for clarity.)
"{
clientId :\"abc\",
note:\"ATTN:Please take care of item x\"
}"
I am trying to fix it (reformating to valid JSON) using javascript regular expression. I am currently using following regular expression but its not working for second property i.e. note as it has colon (:) in its value.
retObject.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2": ');
What I am trying to do here is using regular expression to reformat above string to
"{
"clientId" :"abc",
"note":"ATTN:Please take care of item x"
}"
Tried many ways but couldnt get it just right as I am still beginer in RegEx.
Try using .split() with RegExp /[^\w\s\:]/ , .test() with RegExp /\:$/ , .match() with RegExp /\w+/
var str = "{clientId :\"abc\",note:\"ATTN:Please take care of item x\"}";
var res = {};
var arr = str.split(/[^\w\s\:]/).filter(Boolean);
for (var i = 0; i < arr.length; i++) {
if ( /\:$/.test(arr[i]) ) {
res[ arr[i].match(/\w+/) ] = arr[i + 1]
}
}
console.log(res)
Trying to fix broken JSON with a regexp is a fool's errand. Just when you think you have the regexp working, you will be presented with additional gobbledygook such as
"{ clientId :\"abc\", note:\"ATTN:Please take \"care\" of item x\" }"
where one of the strings has double quotes inside of it, and now your regexp will fail.
For your own sanity and that of your entire team, both present and future, have the upstream component that is producing this broken JSON fixed. All languages in the world have perfectly competent JSON serializers which will create conformant JSON. Tell the upstream folks to use them.
If you have absolutely no choice, use the much-reviled eval. Meet evil with evil:
eval('(' + json.replace(/\\"/g, '"') + ')')

Categories