Encountering a JavaScript toJSON custom method causing a StackOverflow error

Unique Scenario

Upon discovering this answer, a new idea struck me - creating an object from a JSON literal.

This led me to believe I could do the opposite using the handy JSON method: JSON.stringify(myObject).

Curious, I proceeded as follows:

function MyObject(id, value, desc)
{
  this.id = id;
  this.value = value;
  this.desc = desc;
  this.toJSON = function()
  {
    return JSON.stringify(this);
  }

}

However, upon running this code (demo), it threw a Maximum call stack size exceeded error.

After some searching, I came across two resources explaining this behavior:

It seems that .toJSON overrides the .stringify method, leading to a loop when called within itself.

Thought-provoking Questions

  1. (general) Why was the design choice made for toJSON? Is it a reserved or special keyword?
  2. (specific) To resolve the stack overflow issue, I opted to rename .toJSON to .display, which felt somewhat inelegant. Are there alternative solutions?

Answer №1

Seems like the issue is related to toJSON being semi-reserved: stringify checks if the object has a method called toJSON and then attempts to call it to stringify the result.


A possible workaround could be: (Although the reliability of this code is uncertain)

var obj = {
    value: 1,
    name: "John",
    toJSON: function() {
        var ret,
            fn = this.toJSON;

        delete this.toJSON;

        ret = JSON.stringify(this);

        this.toJSON = fn;

        return ret;
    }
}

Example:

obj.toJSON(); // "{\"value\":1,\"name\":\"John\"}"
obj.lastName = "Smith";
obj.toJSON(); // "{\"value\":1,\"name\":\"John\",\"lastName\":\"Smith\"}"

Alternatively, using a closure may provide a cleaner solution: (Which I believe is safe)

var obj = {
    value: 1,
    name: "John",
    toJSON: (function() {
        function fn() {
            var ret;
            delete this.toJSON;

            ret = JSON.stringify(this);

            this.toJSON = fn;

            return ret;
        }
        return fn;
    })()
}

After reading @filmor's comment, I considered another approach to address this issue. Although not elegant, it gets the job done.

By utilizing Function.caller, I can determine if fn is invoked using JSON.stringify

var obj = {
    value: 1,
    name: "John",
    toJSON: (function() {
        return function fn() {
            var ret;

            delete this.toJSON;

            ret = JSON.stringify(this);

            if ( fn.caller === JSON.stringify ) {
                ret = JSON.parse( ret );
            }

            this.toJSON = fn;

            return ret;
        }
    })()
}

Answer №2

Is the method toJSON considered reserved?

I'm uncertain of its reservation status, but one example is the native Date object utilizing toJSON to generate a formatted date string:

(new Date()).toJSON();           // -> "2019-05-22T09:30:42.123Z"
JSON.stringify({d: new Date()}); // -> {"d":"2019-05-22T09:30:42.123Z"}"

A simple workaround:

Create a custom stringify function that disregards any toJSON methods (it can be incorporated into the existing global JSON):

JSON.customStringify = function (obj) {

    var fn = obj.toJSON;
    obj.toJSON = undefined;
    var json = JSON.stringify(obj);
    obj.toJSON = fn;
    return json;
}

Now this can be easily applied to all objects:

function CustomObject(id, value, desc)
{
  this.id = id;
  this.value = value;
  this.desc = desc;
  this.toJSON = function()
  {
    return JSON.customStringify(this);
  }
}

To enhance simplicity further, include:

JSON.customStringifyMethod = function () {

    return JSON.customStringify(this);
}

Objects can now be structured as follows:

function CustomObject(id, value, desc)
{
  this.id = id;
  this.value = value;
  this.desc = desc;
  this.toJSON = JSON.customStringifyMethod;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to send data to an API controller using AJAX in an MVC framework?

I am facing an issue with POSTing a string data to the api controller in mvc using ajax. Despite my efforts, the data does not seem to reach the api controller. Here is what I have attempted: This is the JavaScript code I have used: ...

Exploring MongoDB through proxyquire

To simulate a MongoDB dependency using proxyquire in my testing scenario, I have the following code snippet: var proxyquire = require('proxyquire'); var controller = path.resolve('.path/to/controller/file.js'); inside the before each ...

Enhancing React URLs

Our company deals with URLs in this format: http://helloworld.com/product?filter[category][0]=persian We aim to transform the URL into a cleaner version: http://helloworld.com/product-persian When additional filters are added to the current UR ...

Display Material checkbox based on a condition

I have integrated Material UI into my React application to dynamically display text and other information by using JSON data. { "included": [{ "name": "someName", "price": "0", "required": true } ...

Icon for TypeScript absent from npm package listings

Recently, I created a package and uploaded it to the npm repository. The package was displayed with an icon labeled "ts" on the website. https://i.stack.imgur.com/LoY1x.png The accompanying package.json showcased the inclusion of the "ts" icon - https:// ...

Utilizing JavaScript to showcase information retrieved from the database

After implementing this code example for a cascaded drop-down menu, I would like to incorporate the names of individuals residing in a specific city. How can I achieve this functionality once a city is selected? Demo link: Complete code snippet below: ...

What are the steps to address unhandled promise rejections?

Issue: UnhandledPromiseRejectionWarning: SyntaxError: Unexpected token o in JSON at position 1 Currently working on a MERN stack application. The signup form is in the Frontend, and below is the POST method for it. const onSignUp = async (e) => { ...

What is the best way to extract all <tr> elements from a <tbody> and then convert them into a String?

Below is an example of an HTML table: <table id="persons" border="1"> <thead id="theadID"> <tr> <th>Name</th> <th>sex</th> <th>Message</th> </ ...

I am retrieving data from a service and passing it to a component using Angular and receiving '[object Object]'

Searching for assistance with the problem below regarding my model class. I've attempted various approaches using the .pipe.map() and importing {map} from rxjs/operators, but still encountering the error message [object Object] export class AppProfile ...

Send a single piece of data using AJAX in Flask

I have a very basic HTML form containing only one <input type='text'> field for entering an email address. I am trying to send this value back to a Python script using AJAX, but I am having trouble receiving it on the other end. Is there a ...

JS Nav Dots are not activating the Active Class

I have been utilizing a code snippet from this source to incorporate a vertical dot navigation feature into a single-page website. The navigation smoothly scrolls to different sections when a link is clicked, with an active highlight on the current section ...

What could be the reason for the malfunction of this AngularJS data binding feature?

I am trying to create an angularjs filter that outputs HTML, similar to what is discussed in the link, but I am encountering issues. In my HTML code, I have: <ul> <li ng-repeat="book in books | filter:query"> {{book.title}} ...

Nextjs optimizing page caching to reduce unnecessary rendering

Within my Next.js application, I have implemented two unique pages. Each page is designed to display a randomly selected person's name when the component is initially loaded. simpsons.tsx export default function Simpsons() { const [person, setPerso ...

Unexpected behavior from Internet Explorer - Span contents remain unchanged despite valid input

I have a simple question because I'm feeling a bit lost. Check out this JSFiddle link It seems that in Internet Explorer, the contents of my span won't update even though the input is valid. However, in other browsers, the span content changes ...

How can you retrieve the current user in express.js when incorporating socket.io?

I have a web app using the express framework and socket.io for real-time chat. I am trying to get the current user's username and create a room with them in it, but I am struggling to retrieve the user info when socket.on('connection') is ca ...

Node.js's async functions seem to be running sluggishly

My list of queries is all set and ready to go: var array = [ 'UPDATE EVALUATION SET mark = "16" WHERE id_eval = "21" AND id_usr = "125"', 'UPDATE EVALUATION SET mark = "9" WHERE id_eval = "22" AND id_usr = "125"', ...

Repeating X and Y Axis Labels on Highcharts

Highchart is new to me. I recently created a basic chart showing the count of males and females over the past five years. I have included a screenshot for reference. I am wondering if it's possible to remove duplicate labels from both axes? Below is ...

Ways of assigning a null value to a key in a JSON body request

I am facing an issue with passing a null value to a key using a POST request in an API. I want to send JSON data where Exp and TeamID should be null, like below: { "ID":162617, "TextKey":"107737", "Exp":nul ...

Issue with Redirecting in React: REST requests are not successful

There's a text input that triggers a submission when the [Enter Key] is pressed. const [ query, setQuery ] = React.useState('') ... <TextField label="Search Codebase" id="queryField" onChange={ event => setQuery( ...

Creating an Editor for Input Text Field in HTML: A Step-by-Step Guide

In the vast landscape of JS libraries that can achieve this function, like Trumbowyg and more. However, prior to my rails project displaying that slim version, I need to ensure JavaScript is properly escaped! Therefore, I need to create an editor using o ...