Working with EJSON on Tizen

Introduction

EJSON is extension of JSON that gives developers possibility to store in the JSON format more types than standard Boolean, Number and String values. By default EJSON supports storing Date and Binary (Uint8Array) data. However you can write custom extensions that support your own data formats.

Installation

The EJSON library can be installed as the NPM package

npm install --save ejson

You can also download proper files (libraries) by hand and include them in the HEAD section in the following order.

  1. Underscore - http://underscorejs.org/underscore.js
  2. Base64 - https://raw.githubusercontent.com/meteor/meteor/devel/packages/base64/base64.js
  3. EJSON - https://raw.githubusercontent.com/meteor/meteor/devel/packages/ejson/ejson.js
<body>

  <!-- Your HTML goes here... -->

  <script src="js/lib/underscore.js"></script>
  <script src="js/lib/base64.js"></script>
  <script src="js/lib/ejson.js"></script>
  <script src="js/main.js"></script>
</body>

Usage

The EJSON library provide an EJSON object that is similar to standard JSON object and has two main functions: stringify() and parse(). They have the same purpose as original once. The stringify() function a converts given object into string. The parse() method is converting an EJSON string into original object. Let's see it in action.

var person = {
    firstName: 'John',
    lastName: 'Smith',
    birthDate: new Date(1988, 1, 18)
};

var ejsonStr = EJSON.stringify(person);

var personObj = EJSON.parse(ejsonStr);

The ejsonStr variable contains following string:

"{"firstName":"John","lastName":"Smith","birthDate":{"$date":572137200000}}"

After parsing the string we get the original object with a date value as an object of the Date type.

Object {
    firstName: "John",
    lastName: "Smith",
    birthDate: Thu Feb 18 1988 00:00:00 GMT+0100 (CET) // Date object
}

We can execute Date functions on it.

personObj.birthDate.getFullYear(); // 1988

How does it work?

When using the stringify() function, the object passed to it as a first argument is treated with the EJSON.toJSONValue() function which treats defined types especially. It converts given object to the JSON format but add special fields that instructs the parse() function how to treat given data. As you can see, in the stringified Date, we have special $date attribute which is just a timestamp value. This timestamp is sufficient amount of data to reconstruct the original Date object.

Custom types

Let's assume we have a Person constructor that has fields firstName, lastName and birthDate and two functions greet() and getAge(). We can store birth year of given person and use the getAge() function to retrieve person's current age. If we stringify this object and later parse using standard JSON functions we lose all the functionality. Let's examine how to create our own type that will be stored in the JSON string with the possibility to parse it to the original object without losing any functionality.

First let's define the Person constructor.

var Person = function (data) {
  if (data) {
    if (data.firstName) this.firstName = data.firstName;
    if (data.lastName) this.lastName = data.lastName;
    if (data.birthYear) this.birthYear = data.birthYear;
  }
};

Person.prototype.greet = function () {
  return 'Hi, my name is ' + this.firstName + ' ' + this.lastName + '!';
};

Person.prototype.getAge = function () {
  return (new Date()).getFullYear() - this.birthYear;
};

As you can see it's pretty simple object with two methods. To make it EJSON compatible we have to add at least two methods typeName() and toJSONValue(). The typeName() function just returns unique type name. It can be the name of the constructor - 'Person' in our example. Another method that have to be implemented is toJSONValue() which just returns an object converted to the JSON compatible format. When creating the Date type we would return a timestamp in this method.

There are two more functions that can be provided: clone() and equals(). They do what their names say. The first one clones an object so that any change to the original object will not cause any change in its clone. The second method checks if objects are equal. We can check here if an object is of the given instance and if all fields of both objects are of the same value.

Person.prototype.typeName = function () {
  return 'Person';
};

Person.prototype.toJSONValue = function () {
  return {
    firstName: this.firstName,
    lastName: this.lastName,
    birthYear: this.birthYear,
  };
};

Person.prototype.clone = function () {
  return new Person(this);
};

Person.prototype.equals = function (other) {
  if (!(other instanceof Person)) return false;
      
  return other.firstName === this.firstName && other.lastName === this.lastName;
};

Now we have to add our new type to the list of custom types. We do it using EJSON.addType() function. We pass to it a type name and the fromJSONValue() function which does exactly opposite operation than toJSONValue() function.

EJSON.addType('Person', function fromJSONValue(value) {
  return new Person(value);
});

Now we can play with our new type.

var p1 = new Person({
  firstName: 'John',
  lastName: 'Smith',
  birthYear: 1988
});

var s = EJSON.stringify(p1);
var p2 = EJSON.parse(s);

console.log('Original', p1); // Original Person {firstName: "John", lastName: "Smith", birthYear: 1988, greet: function, getAge: function…}
console.log('Stringified', s); // Stringified {"$type":"Person","$value":{"firstName":"John","lastName":"Smith","birthYear":1988}}
console.log('Parsed', p2); // Parsed Person {firstName: "John", lastName: "Smith", birthYear: 1988, greet: function, getAge: function…}

console.log(p2.greet()); // Hi, my name is John Smith!
console.log(p2.getAge()); // 26

Summary

Creating custom type in the EJSON format is nice way of storing data within the JSON format without losing any functionality of original objects. We can save a strigified data in the local storage or send it to the MongoDB database. We can also store it in the local MiniMongo database which was described in this article https://developer.tizen.org/documentation/articles/using-local-minimongo-database-tizen. Possibilities are endless.