I have an object, i.e.:
{
item1: "value1",
item2: "value2",
item3: {
item4: "value4",
item5: "value5"
}
}
I want to use JSON.stringify
with a replacer
function that will act different on items 4 & 5, the inner properties of item3.
How can that be done ?
something like the following pseudo-code:
return JSON.stringify(obj, (key, val) => {
if (key is child of Item3) {
return someOtherValue;
} else {
return val;
}
}
The desired output is json. i.e.:
{
"item1" : "value1",
"item2" : "value2",
"item3" : {
"item4" : "theSomeOtherValue",
"item5" : "theSomeOtherValue"
}
Edit:
Items 4 & 5 are not known beforehand, they are dynamically generated.
I only know the title for item3
at run time
There are at least two approaches you can take here.
In the replacer function, this
is the object being processed, so when item4
and item5
are being processed, this
refers to the item3
object. So if there's anything about that object that lets you identify it, you can do that by looking at this
. (Be sure to use a traditional function, not an arrow functions, so that JSON.stringify
can set what this
is during the replacer call.)
The replacer function is called with the key and value being processed, so if the key of the object ("item3"
) is unique, you could do special processing when you see it.
Here are a couple of examples of #1:
For instance, if you have a reference to the object, you can compare this
to obj.item3
:
const obj = {
item1: "value1",
item2: "value2",
item3: {
item4: "value4",
item5: "value5"
}
};
console.log(JSON.stringify(obj, function(key, value) {
// ^^^^^^^^^−−−−− Traditional function, not arrow function
if (this === obj.item3) {
console.log("do something different, it's " + key);
return "theSomeOtherValue";
}
return value;
}));
If you don't have a reference to it, you can use any other identifying information you have about it. For instance, with the example data, you can see that it has item4
and item5
properties:
console.log(JSON.stringify({
item1: "value1",
item2: "value2",
item3: {
item4: "value4",
item5: "value5"
}
}, function(key, value) {
// ^^^^^^^^^−−−−− Traditional function, not arrow function
if (this.hasOwnProperty("item4") && this.hasOwnProperty("item5")) {
console.log("do something different, it's " + key);
return "theSomeOtherValue";
}
return value;
}));
Those are just two examples, though; the key thing is that this
is the object being stringified.
Here's an example of #2:
console.log(JSON.stringify({
item1: "value1",
item2: "value2",
item3: {
item4: "value4",
item5: "value5"
}
}, (key, value) => { // It's okay if this one is an arrow function, we're not relying on
// `JSON.stringify` setting `this` for us
if (key === "item3") {
return {
item4: "theSomeOtherValue",
item5: "theSomeOtherValue"
};
}
return value;
}));
If you need the full path of the object being processed, that's a bit more of a pain, but you can get it:
let paths = new Map();
console.log(JSON.stringify({
item1: "value1",
item2: "value2",
item3: {
item4: "value4",
item5: "value5"
},
item6: {
item3: {
item4: "non-special item4",
item5: "non-special item5"
}
}
}, function(key, value) {
// ^^^^^^^^^−−−−− Traditional function, not arrow function
const path = paths.get(this);
// Special processing for the properties of root.item3
if (path === "root.item3") {
return key === "item4" || key === "item5"
? "theSomeOtherValue"
: value;
}
// Keep track of the path of the object
for (const [k, v] of Object.entries(this)) {
if (typeof v === "object") {
if (path) {
// The regex checks for property names that aren't valid(ish)
// property names so we can use brackets notation
paths.set(v, path + (/^\w+$/.test(k) ? "." + k : `[${JSON.stringify(k)}]`));
} else {
paths.set(v, "root");
}
}
}
return value;
}));