Friday, April 17, 2009

JSON Annotations

Suppose you want a generic parser that will turn JSON into a strongly-typed Java object graph. For certain kinds of object graphs you either have to parse into generic objects and then convert, or hint the parser at every step -- a tedious process. What if those type hints were embedded within the JSON itself?

Option 1. Processing Instructions

This approach specifies a reserved prefix for meta-attributes, of which @type is one. Borrowing an idea from sed's variable syntax for find-and-replace separators, you can wrap the entire payload in one outer object that specifies what the metadata prefix is. That way if a given payload would clash with the default metadata prefix, it can be changed easily.
{ "meta": "@"
  "data": {
    "@type": "java.util.HashMap",
    "Joe" : {
      "@type": "jeoftp.Person"
      "firstName": "Joseph",
      "lastName": "Robinson",
      "age": 45,
      "birthday": new Date(23493822934)
    },
    "Sue" : {
      "@type": "jeoftp.Person"
      ...
    }
  }
}

Option 2. Wrap Typed Objects with Annotations

Another way to annotate objects with their types is to wrap each object that needs specific type information within a meta-object that tells us its type. The parser would need to know how to recognize a meta-object vice a regular one.
{
  "@class" : "java.util.HashMap",
  "@data" : {
    "Joe" : {
      "@class" : "jeoftp.Person",
      "@data" : {
        "firstName": "Joseph",
        "lastName": "Robinson",
        "age": 45,
        "birthday": new Date(23493822934)
      }
    },
    "Sue" : {
      "@class" : "jeoftp.Person",
      "@data" : { ... }
    }
  }
}

Option 3. Embed Annotations in Comments

You could also embed type information in comments. This approach borrows from Javadoc, Python docstrings, etc. This would certainly break some JSON parsers, but the advantage is that it strictly demarcates metadata from data.
{
  /* @type java.util.HashMap */
  "Joe" : {
    /* @type jeoftp.Person */
    "firstName": "Joseph",
    "lastName": "Robinson",
    "age": 45,
    "birthday": new Date(23493822934)
    },
  "Sue" : {
    /* @type jeoftp.Person */
    ...
    }
}
If your type parser was smart enough, perhaps you could even handle generics:
{
  /* @type java.util.HashMap */
  "Joe" : {
    "firstName": "Joseph",
    "lastName": "Robinson",
    "age": 45,
    "birthday": new Date(23493822934)
    },
  "Sue" : {
    ...
    }
}
I haven't settled on one approach yet, but after reading this I'm leaning toward option 1 -- specifying a metadata prefix -- because it is valid in curent JSON syntax, easy to parse, and relatively compact.




No comments: