16 min read

In this article by Ray Rischpater, author of the book JavaScript JSON Cookbook, we show you how you can use strong typing in your applications with JSON using C#, Java, and TypeScript. You’ll find the following recipes:

  • How to deserialize an object using Json.NET
  • How to handle date and time objects using Json.NET
  • How to deserialize an object using gson for Java
  • How to use TypeScript with Node.js
  • How to annotate simple types using TypeScript
  • How to declare interfaces using TypeScript
  • How to declare classes with interfaces using TypeScript
  • Using json2ts to generate TypeScript interfaces from your JSON

(For more resources related to this topic, see here.)

While some say that strong types are for weak minds, the truth is that strong typing in programming languages can help you avoid whole classes of errors in which you mistakenly assume that an object of one type is really of a different type. Languages such as C# and Java provide strong types for exactly this reason.

Fortunately, the JSON serializers for C# and Java support strong typing, which is especially handy once you’ve figured out your object representation and simply want to map JSON to instances of classes you’ve already defined. We use Json.NET for C# and gson for Java to convert from JSON to instances of classes you define in your application.

Finally, we take a look at TypeScript, an extension of JavaScript that provides compile-time checking of types, compiling to plain JavaScript for use with Node.js and browsers. We’ll look at how to install the TypeScript compiler for Node.js, how to use TypeScript to annotate types and interfaces, and how to use a web page by Timmy Kokke to automatically generate TypeScript interfaces from JSON objects.

How to deserialize an object using Json.NET

In this recipe, we show you how to use Newtonsoft’s Json.NET to deserialize JSON to an object that’s an instance of a class. We’ll use Json.NET because although this works with the existing .NET JSON serializer, there are other things that I want you to know about Json.NET, which we’ll discuss in the next two recipes.

Getting ready

To begin, you need to be sure you have a reference to Json.NET in your project. The easiest way to do this is to use NuGet; launch NuGet, search for Json.NET, and click on Install, as shown in the following screenshot:

JavaScript JSON Cookbook

You’ll also need a reference to the Newonsoft.Json namespace in any file that needs those classes with a using directive at the top of your file:

usingNewtonsoft.Json;

How to do it…

Here’s an example that provides the implementation of a simple class, converts a JSON string to an instance of that class, and then converts the instance back into JSON:

using System;
usingNewtonsoft.Json;
 
namespaceJSONExample
{
 
public class Record
{
   public string call;
   public double lat;
   public double lng;
}
class Program
{
   static void Main(string[] args)
     {
       String json = @"{ 'call': 'kf6gpe-9',
       'lat': 21.9749, 'lng': 159.3686 }";
 
       var result = JsonConvert.DeserializeObject<Record>(
         json, newJsonSerializerSettings
           {
       MissingMemberHandling = MissingMemberHandling.Error
         });
       Console.Write(JsonConvert.SerializeObject(result));
 
       return;
       }
}
}

How it works…

In order to deserialize the JSON in a type-safe manner, we need to have a class that has the same fields as our JSON. The Record class, defined in the first few lines does this, defining fields for call, lat, and lng.

The Newtonsoft.Json namespace provides the JsonConvert class with static methods SerializeObject and DeserializeObject. DeserializeObject is a generic method, taking the type of the object that should be returned as a type argument, and as arguments the JSON to parse, and an optional argument indicating options for the JSON parsing. We pass the MissingMemberHandling property as a setting, indicating with the value of the enumeration Error that in the event that a field is missing, the parser should throw an exception. After parsing the class, we convert it again to JSON and write the resulting JSON to the console.

There’s more…

If you skip passing the MissingMember option or pass Ignore (the default), you can have mismatches between field names in your JSON and your class, which probably isn’t what you want for type-safe conversion. You can also pass the NullValueHandling field with a value of Include or Ignore. If Include, fields with null values are included; if Ignore, fields with Null values are ignored.

See also

The full documentation for Json.NET is at http://www.newtonsoft.com/json/help/html/Introduction.htm.

Type-safe deserialization is also possible with JSON support using the .NET serializer; the syntax is similar. For an example, see the documentation for the JavaScriptSerializer class at https://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer(v=vs.110).aspx.

How to handle date and time objects using Json.NET

Dates in JSON are problematic for people because JavaScript’s dates are in milliseconds from the epoch, which are generally unreadable to people. Different JSON parsers handle this differently; Json.NET has a nice IsoDateTimeConverter that formats the date and time in ISO format, making it human-readable for debugging or parsing on platforms other than JavaScript. You can extend this method to converting any kind of formatted data in JSON attributes, too, by creating new converter objects and using the converter object to convert from one value type to another.

How to do it…

Simply include a new IsoDateTimeConverter object when you call JsonConvert.Serialize, like this:

string json = JsonConvert.SerializeObject(p,
newIsoDateTimeConverter());

How it works…

This causes the serializer to invoke the IsoDateTimeConverter instance with any instance of date and time objects, returning ISO strings like this in your JSON:

2015-07-29T08:00:00

There’s more…

Note that this can be parsed by Json.NET, but not JavaScript; in JavaScript, you’ll want to use a function like this:

Function isoDateReviver(value) {
if (typeof value === 'string') {
var a = /^(d{4})-(d{2})-(d{2})T(d{2}):(d{2}):(d{2}(?:.d*)?)(?:([+-])(d{2}):(d{2}))?Z?$/
.exec(value);
if (a) {
     var utcMilliseconds = Date.UTC(+a[1],
         +a[2] - 1,
         +a[3],
         +a[4],
         +a[5],
         +a[6]);
       return new Date(utcMilliseconds);
   }
}
return value;
}

The rather hairy regular expression on the third line matches dates in the ISO format, extracting each of the fields. If the regular expression finds a match, it extracts each of the date fields, which are then used by the Date class’s UTC method to create a new date.

Note that the entire regular expression—everything between the/characters—should be on one line with no whitespace. It’s a little long for this page, however!

See also

For more information on how Json.NET handles dates and times, see the documentation and example at http://www.newtonsoft.com/json/help/html/SerializeDateFormatHandling.htm.

How to deserialize an object using gson for Java

Like Json.NET, gson provides a way to specify the destination class to which you’re deserializing a JSON object.

Getting ready

You’ll need to include the gson JAR file in your application, just as you would for any other external API.

How to do it…

You use the same method as you use for type-unsafe JSON parsing using gson using fromJson, except you pass the class object to gson as the second argument, like this:

// Assuming we have a class Record that looks like this:
/*
class Record {
private String call;
private float lat;
private float lng;
   // public API would access these fields
}
*/
 
Gson gson = new com.google.gson.Gson();
String json = "{ "call": "kf6gpe-9",
"lat": 21.9749, "lng": 159.3686 }";
Record result = gson.fromJson(json, Record.class);

How it works…

The fromGson method always takes a Java class. In the example in this recipe, we convert directly to a plain old Java object that our application can use without needing to use the dereferencing and type conversion interface of JsonElement that gson provides.

There’s more…

The gson library can also deal with nested types and arrays as well. You can also hide fields from being serialized or deserialized by declaring them transient, which makes sense because transient fields aren’t serialized.

See also

The documentation for gson and its support for deserializing instances of classes is at https://sites.google.com/site/gson/gson-user-guide#TOC-Object-Examples.

How to use TypeScript with Node.js

Using TypeScript with Visual Studio is easy; it’s just part of the installation of Visual Studio for any version after Visual Studio 2013 Update 2. Getting the TypeScript compiler for Node.js is almost as easy—it’s an npm install away.

How to do it…

On a command line with npm in your path, run the following command:

npm install –g typescript

The npm option –g tells npm to install the TypeScript compiler globally, so it’s available to every Node.js application you write. Once you run it, npm downloads and installs the TypeScript compiler binary for your platform.

There’s more…

Once you run this command to install the compiler, you’ll have the TypeScript compiler tsc available on the command line. Compiling a file with tsc is as easy as writing the source code and saving in a file that ends in .ts extension, and running tsc on it. For example, given the following TypeScript saved in the file hello.ts:

function greeter(person: string) {
return "Hello, " + person;
}
 
var user: string = "Ray";
 
console.log(greeter(user));

Running tschello.ts at the command line creates the following JavaScript:

function greeter(person) {
return "Hello, " + person;
}
 
var user = "Ray";
 
console.log(greeter(user));

Try it!

As we’ll see in the next section, the function declaration for greeter contains a single TypeScript annotation; it declares the argument person to be string. Add the following line to the bottom of hello.ts:

console.log(greeter(2));

Now, run the tschello.ts command again; you’ll get an error like this one:

C:UsersrarischpDocumentsnode.jstypescripthello.ts(8,13): error TS2082: 
Supplied parameters do not match any signature of call target:        Could not apply type 'string' to argument 1 which is         of type 'number'. C:UsersrarischpDocumentsnode.jstypescripthello.ts(8,13): error TS2087:
Could not select overload for 'call' expression.

This error indicates that I’m attempting to call greeter with a value of the wrong type, passing a number where greeter expects a string. In the next recipe, we’ll look at the kinds of type annotations TypeScript supports for simple types.

See also

The TypeScript home page, with tutorials and reference documentation, is at http://www.typescriptlang.org/.

How to annotate simple types using TypeScript

Type annotations with TypeScript are simple decorators appended to the variable or function after a colon. There’s support for the same primitive types as in JavaScript, and to declare interfaces and classes, which we will discuss next.

How to do it…

Here’s a simple example of some variable declarations and two function declarations:

function greeter(person: string): string {
return "Hello, " + person;
}
 
function circumference(radius: number) : number {
var pi: number = 3.141592654;
return 2 * pi * radius;
}
 
var user: string = "Ray";
 
console.log(greeter(user));
console.log("You need " +
circumference(2) +
" meters of fence for your dog.");

This example shows how to annotate functions and variables.

How it works…

Variables—either standalone or as arguments to a function—are decorated using a colon and then the type. For example, the first function, greeter, takes a single argument, person, which must be a string. The second function, circumference, takes a radius, which must be a number, and declares a single variable in its scope, pi, which must be a number and has the value 3.141592654.

You declare functions in the normal way as in JavaScript, and then add the type annotation after the function name, again using a colon and the type. So, greeter returns a string, and circumference returns a number.

There’s more…

TypeScript defines the following fundamental type decorators, which map to their underlying JavaScript types:

  • array: This is a composite type. For example, you can write a list of strings as follows:
    var list:string[] = [ "one", "two", "three"];
  • boolean: This type decorator can contain the values true and false.
  • number: This type decorator is like JavaScript itself, can be any floating-point number.
  • string: This type decorator is a character string.
  • enum: An enumeration, written with the enum keyword, like this:
    enumColor { Red = 1, Green, Blue };
    var c : Color = Color.Blue;
  • any: This type indicates that the variable may be of any type.
  • void: This type indicates that the value has no type. You’ll use void to indicate a function that returns nothing.

See also

For a list of the TypeScript types, see the TypeScript handbook at http://www.typescriptlang.org/Handbook.

How to declare interfaces using TypeScript

An interface defines how something behaves, without defining the implementation. In TypeScript, an interface names a complex type by describing the fields it has. This is known as structural subtyping.

How to do it…

Declaring an interface is a little like declaring a structure or class; you define the fields in the interface, each with its own type, like this:

interface Record {
call: string;
lat: number;
lng: number;
}
 
Function printLocation(r: Record) {
console.log(r.call + ': ' + r.lat + ', ' + r.lng);
}
 
var myObj = {call: 'kf6gpe-7', lat: 21.9749, lng: 159.3686};
 
printLocation(myObj);

How it works…

The interface keyword in TypeScript defines an interface; as I already noted, an interface consists of the fields it declares with their types. In this listing, I defined a plain JavaScript object, myObj and then called the function printLocation, that I previously defined, which takes a Record. When calling printLocation with myObj, the TypeScript compiler checks the fields and types each field and only permits a call to printLocation if the object matches the interface.

There’s more…

Beware! TypeScript can only provide compile-type checking. What do you think the following code does?

interface Record {
call: string;
lat: number;
lng: number;
}
 
Function printLocation(r: Record) {
console.log(r.call + ': ' + r.lat + ', ' + r.lng);
}
 
var myObj = {call: 'kf6gpe-7', lat: 21.9749, lng: 159.3686};
printLocation(myObj);
 
var json = '{"call":"kf6gpe-7","lat":21.9749}';
var myOtherObj = JSON.parse(json);
printLocation(myOtherObj);

First, this compiles with tsc just fine. When you run it with node, you’ll see the following:

kf6gpe-7: 21.9749, 159.3686
kf6gpe-7: 21.9749, undefined

What happened? The TypeScript compiler does not add run-time type checking to your code, so you can’t impose an interface on a run-time created object that’s not a literal. In this example, because the lng field is missing from the JSON, the function can’t print it, and prints the value undefined instead.

This doesn’t mean that you shouldn’t use TypeScript with JSON, however. Type annotations serve a purpose for all readers of the code, be they compilers or people. You can use type annotations to indicate your intent as a developer, and readers of the code can better understand the design and limitation of the code you write.

See also

For more information about interfaces, see the TypeScript documentation at http://www.typescriptlang.org/Handbook#interfaces.

How to declare classes with interfaces using TypeScript

Interfaces let you specify behavior without specifying implementation; classes let you encapsulate implementation details behind an interface. TypeScript classes can encapsulate fields or methods, just as classes in other languages.

How to do it…

Here’s an example of our Record structure, this time as a class with an interface:

class RecordInterface {
call: string;
lat: number;
lng: number;
 
constructor(c: string, la: number, lo: number) {}
printLocation() {}
 
}
 
class Record implements RecordInterface {
call: string;
lat: number;
lng: number;
constructor(c: string, la: number, lo: number) {
   this.call = c;
   this.lat = la;
   this.lng = lo;
}
 
printLocation() {
   console.log(this.call + ': ' + this.lat + ', ' + this.lng);
}
}
 
var myObj : Record = new Record('kf6gpe-7', 21.9749, 159.3686);
 
myObj.printLocation();

How it works…

The interface keyword, again, defines an interface just as the previous section shows. The class keyword, which you haven’t seen before, implements a class; the optional implements keyword indicates that this class implements the interface RecordInterface.

Note that the class implementing the interface must have all of the same fields and methods that the interface prescribes; otherwise, it doesn’t meet the requirements of the interface. As a result, our Record class includes fields for call, lat, and lng, with the same types as in the interface, as well as the methods constructor and printLocation.

The constructor method is a special method called when you create a new instance of the class using new. Note that with classes, unlike regular objects, the correct way to create them is by using a constructor, rather than just building them up as a collection of fields and values. We do that on the second to the last line of the listing, passing the constructor arguments as function arguments to the class constructor.

See also

There’s a lot more you can do with classes, including defining inheritance and creating public and private fields and methods. For more information about classes in TypeScript, see the documentation at http://www.typescriptlang.org/Handbook#classes.

Using json2ts to generate TypeScript interfaces from your JSON

This last recipe is more of a tip than a recipe; if you’ve got some JSON you developed using another programming language or by hand, you can easily create a TypeScript interface for objects to contain the JSON by using Timmy Kokke’s json2ts website.

How to do it…

Simply go to http://json2ts.com and paste your JSON in the box that appears, and click on the generate TypeScript button. You’ll be rewarded with a second text-box that appears and shows you the definition of the TypeScript interface, which you can save as its own file and include in your TypeScript applications.

How it works…

The following figure shows a simple example:

JavaScript JSON Cookbook

You can save this typescript as its own file, a definition file, with the suffix .d.ts, and then include the module with your TypeScript using the import keyword, like this:

import module = require('module');

Summary

In this article we looked at how you can adapt the type-free nature of JSON with the type safety provided by languages such as C#, Java, and TypeScript to reduce programming errors in your application.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here