Is it possible to prevent one of the attributes of an object from being serialized in Javascript?

Asked

Viewed 353 times

14

Is there any way to prevent one of the attributes of an object from being serialized?

Take as an example the object below. If I do not want propriedade2 be serialized, how could I implement this?

var obj = {
  propriedade1 : 'teste 1',
  propriedade2 : {
    subpropriedade1 : 'teste 2 - 1',
    subpropriedade2 : 'teste 2 - 2'
  },
  propriedade3 : 'teste 3',
  propriedade4 : {
    propriedade1 : 'teste 4 - 1'
  }
}

var obj2 = new Object();
obj2.propriedade1 = 'teste 1';
var prop2 = new Object();
prop2.subpropriedade1 = 'teste 2 - 1';
prop2.subpropriedade2 = 'teste 2 - 2';
obj2.propriedade2 = prop2;
obj2.propriedade3 = 'teste 3';
var prop4 = new Object();
prop4.propriedade1 = 'teste 4 - 1';
obj2.propriedade4 = prop4;

console.log(JSON.stringify(obj));
console.log(JSON.stringify(obj2));

  • Do you want to prevent some properties from being passed to JSON? and you know beforehand what they are?

  • @Sergio, in this case, I know which ones I don’t want, therefore, except those I don’t want, all should be serialized

  • 1

    Interesting question and 3 interesting answers! Everyone wins :)

3 answers

12


Another way is to create a nonenumerable property. Nonenumerable properties are not accessed in links for..in, nor are included in JSON generation.

Example:

var o = {};
Object.defineProperty(o, 'teste', { enumerable: false, writable: true });
o.teste = 10;
console.log(o.teste);          // 10
console.log(JSON.stringify(o)) // "{}"

  • Can this solution have any side effects if the value of the property is enumerable by nature? For example, if the value of the property is a list, would you be setting the list as un-enumerable? This can affect other structures in the code, such as a loop that iterates over this value?

  • I think not, each property has its own Property Descriptors, and they determine the attributes of the property in relation to the object to which it belongs. In this example of mine, if teste were an object, you could enumerate his keys with for..in. But I would never find this object by enumerating the properties of the object o. I don’t know if I’m being clear...

  • Yes, it was clear, but even so it is a care to be taken, because it changes the behavior of the object. Anyway, knowledge the most aggregated.

  • Great! I had no idea this existed

9

To simplify understanding, I will create an example closer to reality:

const obj = {
    "name": "John Doe",
    "age": 34,
    "parents": [
        {
            "name": "Derp",
            "age": 63,
            "gender": "male"
        }, {
            "name": "Derpina",
            "age": 62,
            "gender": "female"
        }
    ]
};

Assuming the intention is to serialize the object obj ignoring the attribute age. One detail is that not only the object itself has the attribute age, as also children objects possess (in this case, the two objects in parents). If the intention in this case is to remove all attributes age, no matter where you are, there are two solutions (others have been addressed in the other answers):

First solution: pass as the second parameter of JSON.stringify a list of attributes you want to keep when serializing the object. However, this list of attributes should not only address the attributes of the original object, but all attributes of all related objects that it is desired to maintain. For example, the internal objects a parents have the attribute gender that the main object does not possess and yet such an attribute must be listed:

const obj = {
    "name": "John Doe",
    "age": 34,
    "parents": [
        {
            "name": "Derp",
            "age": 63,
            "gender": "male"
        }, {
            "name": "Derpina",
            "age": 62,
            "gender": "female"
        }
    ]
};

console.log(JSON.stringify(obj, ["name", "parents", "gender"]));

If gender is not listed, the attribute will be removed from the internal objects when serializing.

Second solution: also using the second parameter of JSON.stringify, but now defining a function that will execute the logic of whether or not to keep a value in the object. The function takes two parameters: the key, attribute name, and its value. The logic here would be very simple: if the key matches the attribute we want to remove, we return the value undefined, otherwise returns the value itself.

const obj = {
    "name": "John Doe",
    "age": 34,
    "parents": [
        {
            "name": "Derp",
            "age": 63,
            "gender": "male"
        }, {
            "name": "Derpina",
            "age": 62,
            "gender": "female"
        }
    ]
};

console.log(JSON.stringify(obj, function (key, value) {
    if (key == "age") {
        return undefined;
    }
    
    return value;
}));

This form is relatively more versatile than the first, because instead of specifying which attributes you want to keep, you specify which attributes you want to remove, regardless of the structure of the rest of the object.

Not modifying internal objects

Both solutions remove the attribute from the innermost objects, so if the intention is to keep them, by removing the attribute from the main object, other solutions are needed.

Function toJSON: as presented in response from L.Albano, it is possible to define a function toJSON in the object that will be called to serialize it. The result of serialization will actually be the serialization of the return of this function. L. Albano showed how to clone the object manually, but it is possible to do it dynamically and then remove the desired attribute.

const obj = {
    "name": "John Doe",
    "age": 34,
    "parents": [
        {
            "name": "Derp",
            "age": 63,
            "gender": "male"
        }, {
            "name": "Derpina",
            "age": 62,
            "gender": "female"
        }
    ],
    toJSON: function () {
        
        // Clona o objeto:
        const clone = Object.assign({}, this);
        
        // Remove o atributo desejado:
        delete clone["age"];
        
        // Retorna o clone modificado:
        return clone;
    }
};

console.log(JSON.stringify(obj));

Thus, only the attribute age of the main object is removed, while the internal objects are maintained.

Clone the object using Object.assign only possible from Ecmascript 5 (ES6), but if desired, there are polyfills. Another technique of cloning objects is matching JSON.parse with JSON.stringify, but this technique is not applicable in this example because it would generate an infinite recursion.

  • Just one question, this function syntax like the one you passed in the second example is available in all browsers?

  • This solution directly affects internal objects and may not work in all cases (because the attributes of these objects are also filtered). I’m checking this problem.

  • @Arturotemplario almost all, I will edit the answer as soon as possible.

  • Unfairly scores this answer in relation to the others!

9

I still can’t comment, so just adding the @Andersoncarloswoss response. A third option is the object itself decide which properties it wishes to serialize:

var obj = {
  prop1: "String",
  prop2: 1,
  prop3: true,
  toJSON: function(){
    return {
      prop1: this.prop1,
      prop2: this.prop2
    };
  }
};

console.log(JSON.stringify(obj));

  • I liked it, I didn’t know it worked. I read that it’s not standard (really toJSON is not in Object.prototype), but the latest specification is to see if there is a method with that name when trying to serialize anything.

  • 1

    @bfavaretto, exactly, toJSON is not part of Object.prototype, it is a property sought at the time of serialization, where in addition to the name tbm it is verified whether the type is a function and so this is called for custom serialization. doc

  • Waiting for you to finish so I can give my final judgment

  • Very good, +1 too

Browser other questions tagged

You are not signed in. Login or sign up in order to post.