4

Consider that you get this JSON object:

{ id: 3, name: 'C' }

How can you tell if it's a Vitamin object or a Programming language object. Am I clear?

In typed languages, we simply understand the nature of the object (the Type of the object) from its very name. Each object has a name. How we might achieve something similar with JSON? How can we give names to JSON objects? Any established pattern?

Saeed Neamati
  • 35,341
  • 41
  • 136
  • 188
  • may be {name: 'bla bla', etc ..} –  May 30 '12 at 10:12
  • Sure, that's the easiest way coming to mind. Any other idea? Something like creating a custom JSON parser for example. – Saeed Neamati May 30 '12 at 10:15
  • What if I have three JSON objects like `{ id: 2, name: 'something' }` and the first one refer to a state, the second one to a city, and the third one to a store in that city? How can I differentiate them? Not necessarily needing prototype manipulation. – Saeed Neamati May 30 '12 at 10:22
  • http://javascriptweblog.wordpress.com/2010/06/07/understanding-javascript-prototypes/ –  May 30 '12 at 10:25

3 Answers3

6

If you have this problem it means that your serialization format is not properly defined. You should always be able to deserialize from a JSON object without any trouble.

There are two options:

Add a type field: You can't create the typed object because there are multiple types with the same set of field names. By adding a type field, there is no question about the underlying type of the object. With this option, you will have to go through the process of handling the type field when creating the real object.

Use context to decide what type the object is: If the query you submitted implies that only one type of object should be returned, then there is no problem. Alternately, if multiple types can be returned, the response format should group the objects by type.

{
  "states": [ { id: 3, name: 'New York' } ],
  "cities": [ { id: 4, name: 'New York' } ]
}
unholysampler
  • 17,141
  • 7
  • 47
  • 64
  • 1
    This is shortage of JSON. While ago I asked a question [comparing JSON with XML](http://programmers.stackexchange.com/questions/108740/can-we-replace-xml-with-json-entirely), and now I see that in this regard, XML is superior. – Saeed Neamati May 30 '12 at 11:27
  • 3
    @SaeedNeamati: I think part of the problem is that nobody in the other question pointed out that an xml tag and json curly brackets are similar (and the correct translation), but not equivalent. Curly brackets define a dictionary, it is up to you to give the dictionary context. To the same effect, if you used an xml tag of `item` for all responses instead of `city` and `state`, you would have the same problem you have now with json of not providing enough context. – unholysampler May 30 '12 at 11:48
  • 1
    Alternatively if you think in terms of REST API: the URL itself is the name or the type of resource. So if you are requesting a resource that is named `/states.json` then you should reasonably think it is data that contains states and is of JSON type. – Spoike May 30 '12 at 13:02
3

You have to add a field describing the type of the object. That way you can never have doubts about the type. Look for example at Google's Calendar API. Google's calendar resources all have a field "kind" describing the type.

A Calendar Event looks like:

{
    "kind": "calendar#event",
    "etag": etag,
    "id": string,
    "created": datetime,
    "updated": datetime,
    "summary": string,
    ...
}

A Calendar List Entry like:

{
    "kind": "calendar#calendarListEntry",
    "etag": etag,
    "id": string,
    "summary": string,
    "description": string,
    "location": string,
    ...
}

etc.

Jos de Jong
  • 6,602
  • 3
  • 38
  • 58
1

There's no way to do it for JSON fetched from somewhere else.

If you have control over the JSON, then you can do this:

  1. Add a "type" field to each object.

  2. Tailor make a JSON function to handle this. This can be done in two ways, one secure, one insecure.

Secure method

Create a function stringifyJSONType(). This one stringifies as usual, but adds a type parameter on-the-fly.

 function stringifyJSONType(o){
  o.type=o.constructor.name;
  var s=JSON.stringify(o);
  delete o.type; //To be clean and not modify the object.
  return s;
 }

Now, in the "secure" method, we have to create a switch-case for every type we expect for parsing. This only allows certain types (those which have been kept in the switch-case).

function parseJSONType(s){
 var o=JSON.parse(s);
 switch(o.type){

  case "String":
   o.__proto__=String;
   break;
  case "Date":
   o.__proto__=Date;
   break;
  case "City": //Your custom object
   o.__proto__=City;
   break;  
  case "State": //Your custom object
   o.__proto__=State;
   break;  
  case "Country": //Your custom object
   o.__proto__=Country;
   break;  

/*.... more stuff... */


  case "Object":
  default:
   o.__proto__=Object;
   break;
  
 }
delete o.type;
return o;
}

Now, use these two methods just like JSON.parse() and JSON.stringify(), and it'll work. But for every new type you want to support, you'll have to add an extra case.

Insecure method

Not too insecure, just that it uses the nefarious eval() method. Which isn't too good.. As long as nobody else has the ability to add a custom type parameter to your JSON, it's OK, though.

Here, you use the same stringifyJSONType() as above, but use a different parse method.

 function stringifyJSONType(o){
  o.type=o.constructor.name;
  var s=JSON.stringify(o);
  delete o.type; //To be clean and not modify the object.
  return s;
 }

function parseJSONType(s){
 var o=JSON.parse(s);
 o.__proto__=eval(o.type);
 delete o.type;
 return o;
}

This has the advantage of not requiring switch-case and being easily extended to new types (no code changes required).

Community
  • 1
  • 1
Manishearth
  • 14,882
  • 8
  • 59
  • 76