One of these features is document type definitions. For XML, there are a variety of formats (DTD, XML Schema, RELAX NG, etc.) for specifying exactly what your XML data looks like: what are the tag names, possible attributes, etc. JSON is a lot more loosey-goosey here.
Okay, that's not entirely true: there is JSON Schema. I've never known anyone to use it, but it's there. It's awfully verbose, though. (So are the definitional formats for XML, but it's XML — you expect it!)
I was thinking about this the other day, and I realized that there is actually a great definitional format for JSON already in existence: TypeScript! If you haven't heard of it, TypeScript is a superset of JavaScript which introduces optional strict typing. And since JSON is a subset of JavaScript, TypeScript is applicable to JSON as well.
One of the great features of TypeScript is that interface implementation is implicit. In Java or ActionScript, you have to specifically say that a type "implements MyInterface". In TypeScript, if it fits, it fits. For example:
{
length: number;
}
function isEmpty(list: List): bool
{
return list.length === 0;
}
console.log(isEmpty("")); // true
console.log(isEmpty("foo")); // false
console.log(isEmpty({ length: 0 })); // true
console.log(isEmpty({ length: 3 })); // false
console.log(isEmpty({ size: 1})); // Compiler error!
console.log(isEmpty({ length: 3 })); // false
console.log(isEmpty({ size: 1})); // Compiler error!
(Note: for some reason that I can't fathom, isEmpty() doesn't work on arrays. Well, TypeScript is still in development — version 0.8.2 right now. Update: I filed this as a bug.)
Note that you can use interfaces even on plain objects. So of course you can use it to describe a JSON format. Here's an example from a project I hope to release before too long:
interface Model
{
uid: string;
}
interface Name extends Model
{
citationStart?: number;
html?: string;
namebankID?: string;
root?: bool;
string?: string;
type?: string;
uri?: string;
votes?: number;
}
interface Taxon
{
canonicalName?: Name;
illustrated?: bool;
names?: Name[];
}
Now, for example, I can declare that an API search method will return data as an array of Taxon objects (Taxon[]). And look how compact and readable it is!
Note that there is one drawback here: there is no way to enforce this at run-time. JSON Schema might be a better choice if that's what you need. But for compile-time checking and documentation, it's a pretty great tool.
I would use the following for the List interface:
ReplyDeleteinterface List {
[index: number]: any;
length: number;
}
That actually won't work in this example. For one, the plain object doesn't implement the subscript operator. For another, strings are not guaranteed to implement it in the ECMAScript standard. Both cases yield compile errors (try it in the TypeScript Playground).
DeleteThis, however, will work:
interface List
{
[index: string]: any;
length: number;
}
Basically, any object in JavaScript is considered to implement { [index: string]: any }.