skip to Main Content

I am trying to understand why JSON.stringify shows a different output to the object I am expecting to see.

In my case, my object has a property of height and width

So, if I were to do perform the following

const canvas = new fabric.Canvas("c");
console.log("canvas.width: " + canvas.width)

Then the output would be

canvas.width: 300

However, when I perform

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

the output does not include the width.

In my real situation, I am using fabricjs.com

JSFiddle: https://jsfiddle.net/tdge2908/2/

<canvas id="c"></canvas>  
<div id="result">
</div>

const canvas = new fabric.Canvas("c");
console.log("canvas.isRendering: " + canvas.width)
const result= document.getElementById("result");
result.innerText = JSON.stringify(canvas);

Likewise, I could add something to my canvas object, and it still won’t show when I stringify it.

EG

const canvas = new fabric.Canvas("c");
canvas.myMadeUpThing = "hello";
console.log("canvas.my made up thing: " + canvas.myMadeUpThing ); //this works
const result= document.getElementById("result");
result.innerText = JSON.stringify(canvas);

JSFiddle: https://jsfiddle.net/tdge2908/4/

Why does calling JSON.stringify on this object seem to be doing something unexpected?

2

Answers


  1. The behavior you’re observing is due to how JSON.stringify handles object properties in JavaScript. JSON.stringify only serializes the object’s own enumerable properties, which are the properties that are directly defined on the object itself and are enumerable.

    In the case of Fabric.js objects, many properties (like width and height) are not directly enumerable properties of the canvas object. They are likely accessed through getters and setters or are inherited properties. Therefore, JSON.stringify will not include these properties in its output.

    To demonstrate this, you can check the enumerable properties of an object using Object.keys:

    const canvas = new fabric.Canvas("c");
    console.log(Object.keys(canvas)); // This will show the properties that JSON.stringify would serialize
    

    To include non-enumerable properties in the output, you can use a custom replacer function with JSON.stringify. Here’s an example of how you might achieve this:

    const canvas = new fabric.Canvas("c");
    canvas.myMadeUpThing = "hello";
    
    const replacer = (key, value) => {
      if (key === '') return value; // root object
      if (typeof value === 'object' && value !== null) {
        const allProperties = {};
        for (let prop in value) {
          allProperties[prop] = value[prop];
        }
        return allProperties;
      }
      return value;
    };
    
    const result = document.getElementById("result");
    result.innerText = JSON.stringify(canvas, replacer, 2);
    

    This replacer function will iterate over all properties (including non-enumerable and inherited properties) of each object it encounters and include them in the resulting JSON string.

    However, be cautious with this approach, as it may include a lot of additional properties that you might not want in the JSON string, potentially leading to very large output. Adjust the logic within the replacer function to include only the properties you need.

    Here is an updated version of your JSFiddle to include this behavior:

    <canvas id="c"></canvas>
    <div id="result"></div>
    
    <script>
      const canvas = new fabric.Canvas("c");
      canvas.myMadeUpThing = "hello";
      console.log("canvas.my made up thing: " + canvas.myMadeUpThing); // this works
    
      const replacer = (key, value) => {
        if (key === '') return value; // root object
        if (typeof value === 'object' && value !== null) {
          const allProperties = {};
          for (let prop in value) {
            allProperties[prop] = value[prop];
          }
          return allProperties;
        }
        return value;
      };
    
      const result = document.getElementById("result");
      result.innerText = JSON.stringify(canvas, replacer, 2);
    </script>
    

    This should help you see all the properties, including those like width and your custom property myMadeUpThing.

    Login or Signup to reply.
  2. The object returned by new fabric.Canvas("c") has a toJSON method on its prototype chain. This method — when present — is called by JSON.stringify, and overrides the default behaviour.

    Note that width is an own, enumerable property, so that is not the reason it wouldn’t be included in the JSON output.

    You could (temporarily) set canvas.toJSON to undefined and then run JSON.stringify, but canvas has circular references, so that means JSON.stringify will not be able to serialise it anymore.

    A solution could be to override canvas.toJSON so it returns an object with the properties of interest.

    For example:

    // Define a custum toJSON method for canvas elements:
    fabric.Canvas.prototype.toJSON = function() {
        const { width, height } = this;
        return { width, height };
    };
    
    const canvas = new fabric.Canvas("c");
    console.log(JSON.stringify(canvas));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search