I have a functionality where I enable the user to edit the json object and then validate it in my code, so I have 2 json objects to compare and validate(the initial and the edited one) , I am pushing all the keys from json object into 2 different arrays and then comparing the first array to second and filtering out the missing keys.
The code works for rest of the scenarios(if I remove keys which has an object) but fails if the json has a key which has an array of objects and also starts filtering out the keys which has not been removed.
Ex: conditionals, order when removed starts showing all the keys under conditionals as filtered out
But if I remove keys under alert or offset it shows correct number and name of keys filtered out.
note:- for the stackblitz example I have written same function twice
let defaultJsonFormFields = [];
let editedJsonFormFields = []
function keyList1(obj){
Object.keys(obj).forEach(function (key) {
defaultJsonFormFields.push(key);
if (typeof (obj[key]) == 'object') {
keyList1(obj[key]);
}
});
};
function keyList2(obj){
Object.keys(obj).forEach(function (key) {
editedJsonFormFields.push(key);
if (typeof (obj[key]) == 'object') {
keyList2(obj[key]);
}
});
};
keyList1(defaultjson);
keyList2(editedjson);
console.log(defaultJsonFormFields);
console.log(editedJsonFormFields);
let result = defaultJsonFormFields.filter(item => editedJsonFormFields.indexOf(item) == -1)
console.log(result)
[stackblitzlink]
https://stackblitz.com/edit/js-qkgdoa?file=index.js
let defaultjson =
{
"conditionals": [
{
"order": "1",
"key": "gefvsgwer",
"value": "dghthdffbhrthrdvgrthtuem",
"grpOperation": "wrfllkwjflmefveveveve",
"condition": ">"
}
],
"offset": "15",
"alert": {
"mAlertType": "so new thing",
"mTitle": "abcdef",
"mMessage": "ok so be it",
"mNotificationMsg": "whatever",
"mSeverity": "LOW",
"mEnabled": "true"
}
}
let editedjson = {
"": [
{
"": "1",
"key": "gefvsgwer",
"value": "dghthdffbhrthrdvgrthtuem",
"grpOperation": "wrfllkwjflmefveveveve",
"condition": ">"
}
],
"": "15",
"": {
"": "so new thing",
"mTitle": "abcdef",
"mMessage": "ok so be it",
"mNotificationMsg": "whatever",
"mSeverity": "LOW",
"mEnabled": "true"
}
}
Stackblitz link with actual functionality
https://stackblitz.com/edit/angular-mat-tooltip-x1gtcp?file=app%2Fjson-input.component.ts
On hovering over the first column in table , a tooltip appears , on click of the button dialog box opens up which has edit json section, on click of Add As New Alert I want to check what all keys have been removed from the default json
Update 2:
After a little bit of thinking I realized that RegExp
-s could work in your case without much of a fuss.
Keys in JSON are relatively easy to distinguish from string values:
function test(str, regexp) {
console.log(
Array.from(str.matchAll(regexp), m => m[1])
)
}
// to match a key
test(`{"key1": 1, "key2": 2}`, /\"(.*?)\":/g)
// to match a key followed by spases
test(`{"key1" \n: 1, "key2"\t: 2}`, /\"(.*?)\"\s*:/g)
// to ignore string values
test(`{"key1": "qwer", "key2": 2}`, /\"([^"]*?)\"\s*:/g)
// to ignore escapes in string values
test(`{"key1": "\\"not_a_key\\":", "key2": 2}`, /(?<!\\)\"([^"]*?)(?<!\\)\"\s*:/g)
So back to your code... You can see correct result in the following snippet:
let defaultJsonFormFields = [
"conditionals", "order", "key", "value", "grpOperation", "condition",
"offset",
"alert",
"mAlertType", "mTitle", "mMessage", "mNotificationMsg", "mSeverity", "mEnabled",
]
const editedjson_str = '{\
"": [{\
"": "1",\
"key": "gefvsgwer",\
"value": "dghthdffbhrthrdvgrthtuem",\
"grpOperation": "wrfllkwjflmefveveveve",\
"condition": ">"\
}],\
"": "15",\
"": {\
"": "so new thing",\
"mTitle": "abcdef",\
"mMessage": "ok so be it",\
"mNotificationMsg": "whatever",\
"mSeverity": "LOW",\
"mEnabled": "true"\
}\
}'
let editedjson
// before checking the keys, try to parse editedjson_str
// - no need to report key errors if JSON is invalid in general
//try {
editedjson = JSON.parse(editedjson_str)
//} catch() { ... }
const editedJsonFormFields = Array.from(editedjson_str.matchAll(/(?<!\\)\"([^"]*?)(?<!\\)\"\s*:/g), m => m[1])
let result = defaultJsonFormFields.filter(item => editedJsonFormFields.indexOf(item) == -1)
console.log(result)
But still this approach feels like a dirty hack.
Original answer:
The only thing you need is to log
your editedjson
and you will see that it is not what you expect. Try it:
let editedjson = {
"": [{
"": "1",
"key": "gefvsgwer",
"value": "dghthdffbhrthrdvgrthtuem",
"grpOperation": "wrfllkwjflmefveveveve",
"condition": ">"
}],
"": "15",
"": {
"": "so new thing",
"mTitle": "abcdef",
"mMessage": "ok so be it",
"mNotificationMsg": "whatever",
"mSeverity": "LOW",
"mEnabled": "true"
}
}
console.log(editedjson)
The reason for this is that objects can't contain different value for the same key (and JSON as well). In this case you have in the root of editedjson
3 keys with the same value ""
, and only the last one stays. Others are overwritten.
If you'll clarify what is the exact behavior are you trying to achieve, I can update my answer with the code that will do.
The main issue is that you just can't get from a user an object with keys that are not unique. But you can get a string (and it probably shouldn't be called JSON)... but you really mustn't, because you'll have to parse it by yourself. So you need to choose another format for the data you expect from a user.
Update:
After update to your question I followed your second link to the actual functionality and realized that I was to quick to suggest "the code that will do". As I see, you do receive a string from a user, and as I said you really mustn't. You will get nowhere with JSON.parse()
. And I can not give you a full fledged parser for a not even a JSON with arbitrary errors in it. All I can suggest in this case is:
- The best solution: use forms, with separate field for each value.
As I see in the example, the data have a constant structure (also you compare it to the predefined
defaultjson
). If that's so - there is no reason to complicate things for a user and for yourself
Use regexp-s to check if any key from the defaultJsonFormFields
are missing in the string. Or if there are duplicate keys. And only then try to JSON.parse()
. But you will have hard times with a lot of edge cases. E.g. distinguishing
{ ... "key": "some text" ... }
from
{ ... "wrong key": "key" ... }
- For data of arbitrary structure, just change you approach for error reporting: if there is no
editedjson.conditionals
- report it, and don't try to report that editedjson.conditionals.order
is also missing, but somehow everything is fine with editedjson.conditionals.grpOperation
(because it's not - there is no such path as editedjson.conditionals.grpOperation
as well)
I am not seeing any way to parse this JSON as it needs unique keys and will remove prior duplicate keys.
Basically JSON parser will see this edited json having single property
let editedjson = {
"": [
{
"": "1",
"key": "gefvsgwer",
"value": "dghthdffbhrthrdvgrthtuem",
"grpOperation": "wrfllkwjflmefveveveve",
"condition": ">"
}
],
"": "15",
"": {
"": "so new thing",
"mTitle": "abcdef",
"mMessage": "ok so be it",
"mNotificationMsg": "whatever",
"mSeverity": "LOW",
"mEnabled": "true"
}
}
Output
let editedjson = {
"": {
"": "so new thing",
"mTitle": "abcdef",
"mMessage": "ok so be it",
"mNotificationMsg": "whatever",
"mSeverity": "LOW",
"mEnabled": "true"
}
}
And there is no way you can get back these removed properties.
However there is a way to keep loosing these keys in your existing application. I made some changes in that application
I added another function on "OnChange" event of the text area which will track this object change and will add 'key'+ i (where i will be incremental number) wherever user had removed from this json.
At that time this edited json will look like this:
let editedjson = {
"key1": [
{
"key0": "1",
"key": "gefvsgwer",
"value": "dghthdffbhrthrdvgrthtuem",
"grpOperation": "wrfllkwjflmefveveveve",
"condition": ">"
}
],
"key2": "15",
"key4": {
"key3": "so new thing",
"mTitle": "abcdef",
"mMessage": "ok so be it",
"mNotificationMsg": "whatever",
"mSeverity": "LOW",
"mEnabled": "true"
}
}
Then you can easily compare with your prev value and it should be exact.
Here is the stackblitz link