Currently, I am using the toJSON()
object on a method to ignore any fields that are underscored e.g.
toJSON() {
const properties = Object.getOwnPropertyNames(this);
const publicProperties = properties.filter(property => {
return property.charAt(0) !== '_'
})
const json = publicProperties.reduce((obj, key) => {
obj[key] = this[key]
return obj
}, {})
return json
}
This was fine. But I have the concept of roles in my API and I would like to return private fields if the user is an admin.
This led me to the idea of doing:
toJSON(role='user') {
const properties = Object.getOwnPropertyNames(this);
const publicProperties = properties.filter(property => {
return property.charAt(0) !== '_' || role === 'admin'
})
const json = publicProperties.reduce((obj, key) => {
key = key.charAt(0) === '_' ? key.substring(1) : key
obj[key] = this[key]
return obj
}, {})
return json
}
But then the issue becomes how do I get the role argument passed to the toJSON()
method, especially when JSON.stringify()
is being called and the method calling JSON.stringify()
I might not have access to.
I could set on my object a role
property before returning a json response e.g.
const getCurrentProject = async (c) => {
const project = await projectService.getCurrentProject(c.get('projectId'));
project._role = c.get('payload').role
return c.json(project, httpStatus.OK);
};
But that doesn't seem ideal and then there are more issues when JSON.stringify()
is called on an array of object as I would have to set that for each object.
My next idea was to use my own json response function that would have a replacer function for JSON.stringify()
const jsonResponse = (context, object, status) => {
const role = c.get('payload').role
const body = JSON.stringify(object, (key, value) => {
// function to set private vars to null based on role
})
headers = 'application/json; charset=UTF-8'
return c.body(body, status, headers)
}
The issue with this is that the replacer function will just set them to null and not hide them and I can't just blindly remove keys with null values as I might need them. I could set them to 'remove'
or another placeholder and remove them after but again, it doesn't seem like the best way.
So currently I am confused on what I should do. Is there a way to globally override JSON.stringify()
and add the role parameter as an argument, is there a better approach I am missing? Or should I just stick to the _role
property and for lists of objects set it for each one.
Thanks!
CodePudding user response:
You can use a replacer function. If you return a Function
, Symbol
, or undefined
, the property is not included in the output. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter
CodePudding user response:
I would use Object.entries
, and then Array.filter
the keys you want, finally Object.fromEntries
to get back to an object.
example ->
const obj = {
_adminOnly: 'Lets have a party!',
name: 'bob',
age: 22,
_hideme: 'Hide unless admin',
toJSON: function (role='user') {
return Object.fromEntries(
Object.entries(this).
filter(([k]) => {
if (k === 'toJSON') return false;
return role === 'admin'
? true
: k.charAt(0) !== '_'
}
)
);
}
}
console.log(obj.toJSON());
console.log(obj.toJSON('admin'));