This project is read-only.

JSON HowTo

This page explains, how to utilize features provided by the JSON@CodeTitans library. It will simply go through code snippets presenting all designed code constructions. And as it is shown, there are no barriers or any limitations. All public API methods perform always sanity validations and it is impossible to read and to produce invalid JSON.

Writing overview

The purpose of writing to JSON is a need of serializiation of any .NET class into its textual representation. Then it could be further processed: stored in a persistent storage or transferred to another machine and deserialized. Recently it became popular due to several advantages comparing to other formats:
  • it's extremly simple,
  • it's human readable,
  • format keywords doesn't occupy much space.

JSON standard provides limited number of entities that are required for serialization task. They are: number, string for single data representation and array and object for grouping them. However string can contain value in any format, that is known to the developer. That's why JSON@CodeTitans extends strings with own formats to store common types like: dates, timespans, GUIDs and similar.

How then, the simples scenario look like:
  1. create JSonWriter object
  2. use its public API to start an object definition (using WriteObjectBegin())
  3. write object fields with values (using WriteMember(name,value));
  4. close an object definition (using WriteObjectEnd())
  5. get serialized JSON text (by calling ToString())

or differently, when writing an array of items (notice, that API methods changed)
  1. use the public API to start an array definition (using WriteArrayBegin())
  2. write array values (using WriteValue(value))
  3. close an array definition (using WriteArrayEnd()).
  4. get serialized JSON text (by calling ToString())

or
  1. use the Write() method and let reflection detect the type and way, you want to serialize
Following sections will provide a more useful code snippets.

FCL types writing

So, how about writing array of sizes into JSON. Let's say, the expected result would be just a simple array of strings:

JSON: ["small", "medium", "large", "huge"]

This could be done using following code sequence:

JSonWriter writer = new JSonWriter(); // unindented
writer.Write(new string[] { "small", "medium", "large", "huge" });

Console.WriteLine("JSON: {0}", writer.ToString());

Writing manually an array

In previous example reflection was used to recognize types and perform serialization. Can this be done faster, by using a direct method calls? And the answer is yes. Following sample demonstrates it:

JSonWriter writer = new JSonWriter(); // unindented

writer.WriteArrayBegin();
writer.WriteValue(123);
writer.WriteValue(234);
writer.WriteValue(345);
writer.WriteValue(456);
writer.WriteArrayEnd();

Console.WriteLine("JSON: {0}", writer.ToString());

Generates output:

JSON: [123,234,345,456]

This code however has one disadvantage. It so easy to forget adding proper number of "WriteArrayEnd()" matching "WriteArrayBegin()". Also it's not obvious, when the array starts and when ends, the whole code must be read. The good part is however, that if invalid number of "begin" and "end" is called, than JSonWriter will throw an exception, when ToString() is performed. Let's then see, how some syntax sugar can improve readability:

JSonWriter writer = new JSonWriter(); // unindented

using(writer.WriteArray())
{
    writer.WriteValue(123);
    writer.WriteValue(234);
    writer.WriteValue(345);
    writer.WriteValue(456);
}

Console.WriteLine("JSON: {0}", writer.ToString());

Writing an embedded array

But flat-hierarchy data structures is not what we only look for. Is there a way to build something more complex? Let's say, is it possible to store an array of arrays? And this time again, the answer is yes. It's not only possible, but also it's extremly easy to achieve. Take a look on following sample.

JSonWriter writer = new JSonWriter(); // unindented

writer.WriteArrayBegin();
writer.WriteArrayBegin();
writer.WriteValue(123);
writer.WriteValue(234);
writer.WriteValue(345);
writer.WriteValue(456);
writer.WriteArrayEnd();
writer.WriteArrayBegin();
writer.WriteValue("abc");
writer.WriteValue("def");
writer.WriteValue("ghi");
writer.WriteArrayEnd();
writer.WriteArrayEnd();

Console.WriteLine("JSON: {0}", writer.ToString());

Generates output:

JSON: [[123,234,345,456], ["abc", "def", "ghi"]]

When improved syntax is applied, the output is identical and code looks like:

JSonWriter writer = new JSonWriter(); // unindented

using(writer.WriteArray())
{
    using(writer.WriteArray())
    {
        writer.WriteValue(123);
        writer.WriteValue(234);
        writer.WriteValue(345);
        writer.WriteValue(456);
    }
    using(writer.WriteArray())
    {
        writer.WriteValue("abc");
        writer.WriteValue("def");
        writer.WriteValue("ghi");
    }
}

Console.WriteLine("JSON: {0}", writer.ToString());

And for further notice. Not only arrays can be embedded. Keep reading to get knowledge, how to serialize and deserialize the JSON objects and them mix those structures as you like.

Writing complex object to JSON

JSonWriter writer = new JSonWriter(true); // indented

writer.WriteArrayBegin();
writer.WriteObjectStart();
writer.WriteMember("Name", "Paweł");
writer.WriteMember("Salary", 100);
writer.WriteMember("Company");
writer.WriteObjectBegin();
writer.WriteMember("Name", "CodeTitans");
writer.WriteMember("Address", "ABCD");
writer.WriteMember("Value", 10.28437411e2);
writer.WriteMember("Started", DateTime.Now);
writer.WriteMemberNull("Account");
writer.WriteObjectEnd();
writer.WriteObjectEnd();
writer.WriteArrayEnd();

// retrieve string representation
Console.WriteLine("JSON: {0}", writer.ToString());

output:

JSON: [
    {
        "Name": "Paweł",
        "Salary": 100,
        "Company": {
            "Name": "CodeTitans",
            "Address": "ABCD",
            "Value": 1028.437411,
            "Started": "2010-09-18 21:16:11Z",
            "Account": null
        }
    }
]

All displayed operations could be wrapped inside single 'Write(IJSonWriter output)' method, so that custom class could implement IJSonWritable interface. This interface is at runtime used by JSonWriter to simplify collections serialization.

Object writing and reading without using attributes

class JSonSampleClass : IJSonSerializable
{
    public JSonSampleClass()
    {
        ID = Guid.NewGuid();
    }

    public JSonSampleClass(string name, int age, double salary)
    {
        ID = Guid.NewGuid();
        Name = name;
        Age = age;
        Salary = salary;
    }

    #region Properties

    public Guid ID
    { get; private set; }

    public string Name
    { get; private set; }

    public int Age
    { get; private set; }

    public double Salary
    { get; private set; }

    #endregion

    #region IJSonWritable Members

    public void Write(IJSonWriter output)
    {
        output.WriteObjectBegin();
        output.WriteMember("ID", ID);
        output.WriteMember("Name", Name);
        output.WriteMember("Age", Age);
        output.WriteMember("Salary", Salary);
        output.WriteObjectEnd();
    }

    #endregion

    #region IJSonReadable Members

    public void Read(IJSonObject input)
    {
        ID = input["ID"].GuidValue;
        Name = input["Name"].StringValue;
        Age = input["Age"].Int32Value;
        Salary = input["Salary"].DoubleValue;
    }

    #endregion
}

// ...
JSonWriter writer = new JSonWriter(true); // indented
JSonReader reader = new JSonReader();
writer.Write(new JSonSampleClass("Paweł", 30, 1001.1));

JSonSampleClass result = new JSonSampleClass();
result.Read(reader.ReadAsJSonObject(writer.ToString()));

// and since now on 'result' contains data passed via JSON writer-reader classes...

Serialized JSON object:
{
    "ID": "176b5f11-b514-491e-ba1f-7b470444c425",
    "Name": "Paweł",
    "Age": 30,
    "Salary": 1001.1
}

Serialization using dedicated attributes

[JSonSerializable(AllowAllFields=true, IgnoreStatic=false)]
class SampleAttributedClass
{
    [JSonMember("name", "Unknown")]
    private string _name;
    [JSonMember("age", 18)]
    private int _age;
    [JSonMember("address", "default", SkipWhenNull=true)]
    private string _address;

    [JSonMember("CurrentTime")]
    private DateTime _dt = DateTime.Now;

    private Guid testField;

    public const string Text = "Some Const Text, that is not supposed to be serialized!";
    public static bool StaticSampleField = false;

    public SampleAttributedClass()
    {
    }

    public SampleAttributedClass(string name, int age)
    {
        testField = Guid.NewGuid();
        _name = name;
        _age = age;
    }

    public string Name
    {
        get { return _name; }
    }

    public int Age
    {
        get { return _age; }
    }

    public string Address
    {
        get { return _address; }
    }
}

///////////////////////
// serialize:
SampleAttributedClass o1 = new SampleAttributedClass("Paweł", 20);
writer.Write(o1);

Console.WriteLine("JSON object: {0}", writer.ToString());

///////////////////////
// deserialize:
var o2 = reader.ReadAsJSonObject(writer.ToString()).ToObjectValue<SampleAttributedClass>();

// And since here, o2 is an instance of SampleAttributedClass type filled with the same data as 'o1'.
// Please also note, that JSON contained value for static fields, so they will be updated as well,
// if changed between serialization of 'o1' and deserialization of 'o2'.

JSON object:
{
    "name": "Paweł",
    "age": 20,
    "CurrentTime": "2010-11-28 22:07:28Z",
    "testField": "e88dca60-a367-4eae-a72a-3b13c2605602",
    "StaticSampleField": false
}

Last edited Jul 16, 2011 at 12:07 AM by FeydRauth, version 8

Comments

No comments yet.