Home > Software design >  How to dynamically ignore certain fields in a javascript object when converting it to json?
How to dynamically ignore certain fields in a javascript object when converting it to json?

Time:07-20

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'));

  • Related