17
头图

A little trick about JSON in JavaScript

1. Formatting

The default stringizer also shrinks the JSON, which looks ugly

const user = {
  name: 'John',
  age: 30,
  isAdmin: true,
  friends: ['Bob', 'Jane'],
  address: {
    city: 'New York',
    country: 'USA'
  }
};
console.log(JSON.stringify(user));
//=> {"name":"John","age":30,"isAdmin":true,"friends":["Bob","Jane"],"address":{"city":"New York","country":"USA"}}

JSON.stringify also has a built-in formatter!

console.log(JSON.stringify(user, null, 2));
// {
//   "name": "John",
//   "age": 30,
//   "isAdmin": true,
//   "friends": [
//     "Bob",
//     "Jane"
//   ],
//   "address": {
//     "city": "New York",
//     "country": "USA"
//   }
// }

(If you want to know what that null is, we'll get to that later)

In this example, the JSON format is 2 indented spaces.

We can also specify custom characters for indentation.

console.log(JSON.stringify(user, null, 'lol'));
// {
// lol"name": "John",
// lol"age": 30,
// lol"isAdmin": true,
// lol"friends": [
// lollol"Bob",
// lollol"Jane"
// lol],
// lol"address": {
// lollol"city": "New York",
// lollol"country": "USA"
// lol}
// }

2. Hide some properties in stringified data

JSON.stringify The second parameter, which is largely unknown. It's called replacer, and it's a function or array that decides what data to keep in the output and what not to.

Here is a simple example where we can hide the password user.

const user = {
  name: 'John',
  password: '12345',
  age: 30
};

console.log(JSON.stringify(user, (key, value) => {
    if (key === 'password') {
            return;
    }

    return value;
}));

Here is the output:
{"name":"John","age":30}
We can refactor further:

function stripKeys(...keys) {
    return (key, value) => {
        if (keys.includes(key)) {
            return;
        }

        return value;
    };
}

const user = {
  name: 'John',
  password: '12345',
  age: 30,
  gender: 'male'
};

console.log(JSON.stringify(user, stripKeys('password', 'gender')))

output:
{"name":"John","age":30}
You can also pass an array to get only certain keys:

const user = {
    name: 'John',
    password: '12345',
    age: 30
}

console.log(JSON.stringify(user, ['name', 'age']))

output the same thing.

This also works for arrays. If you have a bunch of cakes:

const cakes = [
    {
        name: 'Chocolate Cake',
        recipe: [
            'Mix flour, sugar, cocoa powder, baking powder, eggs, vanilla, and butter',
            'Mix in milk',
            'Bake at 350 degrees for 1 hour',
            // ...
        ],
        ingredients: ['flour', 'sugar', 'cocoa powder', 'baking powder', 'eggs', 'vanilla', 'butter']
    },
    // tons of these
];

We can easily do the same and the replacer will be applied to each cake:

const cakes = [
    {
        name: 'Chocolate Cake',
        recipe: [
            'Mix flour, sugar, cocoa powder, baking powder, eggs, vanilla, and butter',
            'Mix in milk',
            'Bake at 350 degrees for 1 hour',
            // ...
        ],
        ingredients: ['flour', 'sugar', 'cocoa powder', 'baking powder', 'eggs', 'vanilla', 'butter']
    },
    // tons of these
];

console.log(JSON.stringify(cakes, ['name']))

We get this:
[{"name":"Chocolate Cake"},{"name":"Vanilla Cake"},...]

3. Use toJSON to create a custom output format

If an object implements the toJSON function, JSON.stringify will use it to stringify the data.

think about it:

class Fraction {
  constructor(n, d) {
    this.numerator = n;
    this.denominator = d;
  }
}

console.log(JSON.stringify(new Fraction(1, 2)))

This will output {"numerator":1,"denominator":2}. But what if we want to replace 1/2 of it with a string?

Enter toJSON

class Fraction {
  constructor(n, d) {
    this.numerator = n;
    this.denominator = d;
  }

  toJSON() {
      return `${this.numerator}/${this.denominator}`
  }
}

console.log(JSON.stringify(new Fraction(1, 2)))

JSON.stringify respects the toJSON property and outputs "1/2".

4. Recover data

Our fractional example above works well. But what if we want to recover data? Wouldn't it be cool if the score could be magically returned when we parsed the JSON again? We can!

Enter the Resurrection!

class Fraction {
  constructor(n, d) {
    this.numerator = n;
    this.denominator = d;
  }

  toJSON() {
      return `${this.numerator}/${this.denominator}`
  }

  static fromJSON(key, value) {
    if (typeof value === 'string') {
        const parts = value.split('/').map(Number);
        if (parts.length === 2) return new Fraction(parts);
    }

    return value;
  }
}

const fraction = new Fraction(1, 2);
const stringified = JSON.stringify(fraction);
console.log(stringified);

// "1/2"
const revived = JSON.parse(stringified, Fraction.fromJSON);
console.log(revived);
// Fraction { numerator: 1, denominator: 2 }

We can pass the second parameter JSON.parse to specify the reviver function. The restorer's job is to "restore" the stringified data back to its original form. Here we pass a reviver which is the static fromJSON property Fraction of the class.

In this case, the reviver checks if the value is a valid fraction, and if so, it creates a new Fraction object and returns it.

Fun fact: this functionality is used with the built-in Date object. Try to find Date.prototype.toJSON
Here's why it works:

console.log(JSON.stringify(new Date()))
//=> '"2022-03-01T06:28:41.308Z"'

To restore the date, we can use JSON.parse:

function reviveDate(key, value) {
    const regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,}|)Z$/;

    if (typeof value === "string" && regex.test(value)) {
        return new Date(value);
    }

    return value;
}
console.log(JSON.parse('"2022-03-01T06:28:41.308Z"', reviveDate))
//=> Tue Mar 01 2022 06:28:41 GMT-0700 (Pacific Daylight Time)

5. Use revivers to hide data

Like parsers, restorers can also be used to hide data. It works the same way.

Here is an example:

const user = JSON.stringify({
  name: 'John',
  password: '12345',
  age: 30
});

console.log(JSON.parse(user, (key, value) => {
    if (key === 'password') {
            return;
    }

    return value;
}));

Here is the output:
{ name: 'John', age: 30 }

If you know of any other cool JSON tricks, please let me know.

exchange group

WX20210922-091703.png


雾岛听风
11.9k 声望8.6k 粉丝

丰富自己,胜过取悦别人。