0

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)

Gibanica
  • 61
  • 7
  • 1
    `JSON.parse(str)` for a shallow copy. `password='12345'` is an assignment not key/value – zer00ne Jun 14 '22 at 13:11
  • @zer00ne—the string is not valid JSON, so that isn't going to work. It doesn't even have a regular grammar so a regular expression will struggle too. – RobG Jun 14 '22 at 13:14
  • @RobG I know I just read the input after reading the title. – zer00ne Jun 14 '22 at 13:15
  • It seems you're looking to write a parser, see [this answer](https://stackoverflow.com/a/9448867/257182). – RobG Jun 14 '22 at 13:16
  • _"but in case some of the fields, i.e password contains ',' or '=', my code will break. Any idea on how to approach this?"_ - by changing that nasty data format into something proper first of all. Do not invent your own data storage/transport formats, when perfectly good alternatives like JSON already exist. – CBroe Jun 14 '22 at 13:28
  • What is the syntax if the password has a quote? Is it escaped somehow? – trincot Jun 14 '22 at 13:40
  • @trincot Unfortunately not, none of the special characters is escaped in any way. – Gibanica Jun 14 '22 at 14:23
  • @CBroe I would like to change it, but it's not up to me, I need to work with what I got. I've added additional explanation, I've obviously poorly explained the situation in the original post. – Gibanica Jun 14 '22 at 14:25
  • So how would `password='test',a=b'` be interpreted? As `{password: 'test', a: "b'"}` or as `{password: "test',a=b"}`? And what if I wanted it to be the other interpretation, how then should it be encoded in the input? – trincot Jun 14 '22 at 14:26
  • @trincot The first one. To be completely precise, I need to transfer this: `app: 'localhost=3000,password=12345,ssl=True' ` to this: `{ app_server: 'localhost:3000', app_password: '12345', app_ssl: 'True' }` – Gibanica Jun 14 '22 at 14:34
  • Why your expected output has "localhost:3000" as a value and not `app_localhost` as a key? Is there any logic why you want *localhost* to be treated differently than the other keys in the `key=value` format? This question really lacks clear specification of input and output formats. And it doesn't help to just repeat the example that is already in the question. – trincot Jun 14 '22 at 14:37
  • Also, if it is the first one, then please tell me what the input format would have to be to get the second one. It seems to me that your input format does not allow a value to include a quote. – trincot Jun 14 '22 at 14:38
  • @trincot localhost:3000 is a value for the server, but for some weird reason, it has no 'key', so I need to add that after the first split. The data is poorly structured, as you can see, which make my job harder. – Gibanica Jun 14 '22 at 14:45
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/245602/discussion-between-psycho-buddha-and-trincot). – Gibanica Jun 14 '22 at 14:51
  • Have you retrieved information so you can give clarifications on the point raised? How a quote would be represented in a field like `password` without it being wrongly interpreted as the end of the value? For instance, what if the literal password would be "abc',def=", how would the input string look? – trincot Jun 15 '22 at 12:06

2 Answers2

1

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.

Debug Diva
  • 26,058
  • 13
  • 70
  • 123
  • Clarification from the asker should be asked via the comment section. – trincot Jun 15 '22 at 10:20
  • @trincot Yes you are correct but here I suggested a solution as well if that will work for him. – Debug Diva Jun 15 '22 at 11:37
  • 1
    Thank you @RohìtJíndal, I will need to test it bit more, but based on few simple tests, this will solve my issue. In any case I will try to talk with the rest of the team about improving the data structure and escaping characters, but for now, this works pretty well. Thanks again! – Gibanica Jun 15 '22 at 12:45
0

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));
trincot
  • 317,000
  • 35
  • 244
  • 286