Using typeful JSON Deserialisation and Validation in PowerShell

One of the cool features in PowerShell is the string handling – especially that of XML and JSON, with the help of Cmdlets like “ConvertFrom-Json” and the like. This all works very well when quickly converting small pieces of JSON data. However, as it turns out they have quite some shortcomings, as you can quickly find out for yourself when searching via your favourite search engine “convertfrom json maxjsonlength” and similar. What is furthermore particular annoying is the lack of type awareness. Everything you pass into “ConvertFrom-JSON” becomes a “System.Management.Automation.PSCustomObject” or “PSCustomObject” for short. There are cases when you actually “know” what type of object the JSON data really represents, so why not work with the correct type from the beginning? This especially makes sense when you use PowerShell to work with your own code (such as a scripting extension to your own programme, as we are doing quite often).

So what I would like to show you is the ability to use native C# classes to convert your JSON data into (the same you would do, when having loaded a third party custom assembly as shown in my post about custom ODATA actions). So let’s say we have a “Contact” class that stores a person contact as you can see in the following listing:

// class file name: PowerShellTypefulJsonClass.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace biz.dfch.CSharp
{
  public class Contact
  {
    public Contact()
    {
      System.Diagnostics.Debug.WriteLine(string.Format("biz.dfch.CSharp.Contact::Contact()"));
    }

    private string _FirstName;
    public string FirstName
    {
      get { return _FirstName; }
      set { _FirstName = value; }
    }
    private string _LastName;
    public string LastName
    {
      get { return _LastName; }
      set { _LastName = value; }
    }
    private string _Description;
    public string Description
    {
      get { return _Description; }
      set { _Description = value; }
    }
    private int _Age;
    public int Age
    {
      get { return _Age; }
    }
    private DateTimeOffset _Birthday;
    public DateTimeOffset Birthday
    {
      get { return _Birthday; }
      set { 
      _Birthday = value; 
      // Taken from http://stackoverflow.com/a/1404
      var datNow = DateTimeOffset.UtcNow;
      var age = datNow.Year - _Birthday.Year;
      if (_Birthday > datNow.AddYears(-age)) age--;
      _Age = age;
      }
    }
  }
}

There is nothing special in this class, maybe except it calculates the age of the person when setting the Birthday property. But standard public properties without field encapsulation would work equally well. So the corresponding JSON notation could look like this:

PS > $json = $c | ConvertTo-Json
PS > $json
{
    "FirstName":  "Edgar",
    "LastName":  "Schnittenfittich",
    "Description":  "Some person reappearing in my examples",
    "Age":  44,
    "Birthday":  "\/Date(-3600000)\/"
}

When normally converting such a JSON string to a PowerShell object you would end up with this:

PS > $json | ConvertFrom-Json
FirstName   : Edgar
LastName    : Schnittenfittich
Description : Some person reappearing in my examples
Age         : 44
Birthday    : 12/31/1969 11:00:00 PM

PS > $o1 = $json | ConvertFrom-Json
PS > $o1.GetType()
IsPublic IsSerial Name           BaseType
-------- -------- ----           --------
True     False    PSCustomObject System.Object

PS > $o1.GetType().FullName
System.Management.Automation.PSCustomObject

To retain the true nature of this object you could convert it using the “System.Web.Script.Serialization.JavaScriptSerializer” from the “System.Web.Extensions” assembly …

Add-Type -AssemblyName System.Web.Extensions
$jss = New-Object System.Web.Script.Serialization.JavaScriptSerializer
$jss.MaxJsonLength = [System.Int32]::MaxValue
$jss.RecursionLimit = 99

… load your class file …

$ClassDefinitionFileAndPath = ".\PowerShellTypefulJsonClass.cs"
$TypeDefinition = Get-Content -Raw $ClassDefinitionFileAndPath
Add-Type -TypeDefinition $TypeDefinition;

… and when you convert / deserialse your JSON string, it will end up with the native .NET object it was originally defined as:

PS > $o2 = $jss.Deserialize($json, [biz.dfch.CSharp.Contact]);
PS > $o2.GetType().FullName
biz.dfch.CSharp.Contact

With a class that simple as shown here it might not make a difference, but when working with more complex data types it really eases your work as you not only get the properties converted correctly but you can also benefit from any additional logic that is defined with the classes (such as calculating the birthday as in our example).

Trackbacks

  1. […] every property after conversion is particular tedious and annoying work. Recently I wrote about Using typeful JSON Deserialisation and Validation in PowerShell. However, sometimes this is just not feasible as you might not have a C# class describing your […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: