DEV Community

Pacharapol Withayasakpunt
Pacharapol Withayasakpunt

Posted on

What is a best / simplest way to serialize / deserialize JavaScript objects?

Also, it is best to use minimal libraries. Prefer built-ins, rather than NPM.

I tried to use JSON.stringify with replacer; and JSON.parse with reviver, but it isn't as easy and intuitive as it seems.

This doesn't work, BTW.

function serialize (obj: any) {
  return JSON.stringify(
    obj,
    (_, v) => v instanceof Date ? { __date__: +v } : v
  )
}

function deserialize (s: string) {
  return JSON.parse(
    s,
    (k, v) => k === '__date__' ? new Date(v) : v
}
Enter fullscreen mode Exit fullscreen mode

Solution

function serialize (obj: any) {
  return JSON.stringify(
    obj,
    function (k, v) {
      if (this[k] instanceof Date) {
        return ['__date__', +this[k]]
      }
      return v
    }
  )
}

function deserialize (s: string) {
  return JSON.parse(
    s,
    (_, v) => (Array.isArray(v) && v[0] === '__date__') ? new Date(v[1]) : v
  )
}
Enter fullscreen mode Exit fullscreen mode

Functions should work as well, with Function.toString() and eval(Function).

Questions

  • Somehow, NeDB's internal uses { $$date: +new Date() } with great success. I don't know how that works.
  • Even if I manage to get JSON.parse and JSON.stringify to work with Date, I cannot be really sure if it is more performant than third party solutions, but at least it is better than js-yaml...
  • JSON.stringify / JSON.parse is also known to not preserve key orders. I don't know if this will be problematic in the future.

Top comments (3)

Collapse
 
patarapolw profile image
Pacharapol Withayasakpunt

Actually, I have now succeeded in deserializing objects (instead of Arrays). The solution is here.

GitHub logo patarapolw / any-serialize

Serialize any JavaScript objects, as long as you provides how-to. I have already provided Date, RegExp and Function.

Also, to create a MongoDB-compatible serialize, you can try.

const cond = {
  a: new Date(),
  b: /regexp/gi
}

const r = JSON.stringify(cond, function (k, v) {
  const v0 = this[k]
  if (v0) {
    if (v0 instanceof Date) {
      return { $date: v0.toISOString() }
    } else if (v0 instanceof RegExp) {
      return { $regex: v0.source, $options: v0.flags }
    }
  }
  return v
})

console.log(r)

console.log(JSON.parse(r, (_, v) => {
  if (v && typeof v === 'object') {
    if (v.$date) {
      return new Date(v.$date)
    } else if (v.$regex) {
      return new RegExp(v.$regex, v.$options)
    }
  }
  return v
}))
Enter fullscreen mode Exit fullscreen mode

I also created a Gist for the time being -- gist.github.com/patarapolw/c9fc59e...

Collapse
 
diek profile image
diek

The +new Date() returns the Date object parsed to number, so the same as (new Date()).getTime(). Y supose that it is easy to use, in his/her solution, the unix timestamp instead the string iso notation of the date. I don't know if you mean this.

I have a question to you too, why do you even will care about the keys being ordered?

Collapse
 
patarapolw profile image
Pacharapol Withayasakpunt • Edited
  • Both NeDB and MongoDB can differentiate between a real Date object, and date ISO string.
  • I mean, why special key ($$date), not Array. I cannot even make special Object work.
  • Keys being ordered is important when I try to hash Objects. In that case, I use fast-json-stable-stringify or js-yaml + SparkMD5.