JSON (basics), follow-up to XML

XML is quite verbose. This leads to higher memory usage and slowness during the (de-)serialization process. There are alternatives in the market that also support the cross-platform approach. The most basic one, which is used among web APIs, is JSON. It is well-integrated into JavaScript.
The .Net framework supports JSON. Thus JSON is a good start to slowly delve into posts about networking. There are more advanced cross-platform solutions. Jackson, GSON and BSON are some of the really good ones. Besides this you can always use the good old .Net data serialization (or remoting). If you need speed rather than flexibility, then I would probably recommend to use your own binary protocol. Unfortunately custom binary protocols are error-prone, debugging can be hell and the control factor for IT support people is close to zero in case the protocol is corrupt. Does transmitting data take longer than encoding/packing data? That is the fundamental question. Protocol Buffers will most likely be the “binary number cruncher’s” best friend. It is a free (Apache License) cross plattforms product. There is also a specific version for the .Net environment. Protocol Buffers is not the fastest tool in the market, but all faster tools I know are (like kryo) for Java only. Protocol Buffers is supported by many programming languages. Its speed is pretty good.
As usual plenty of posts lie ahead of us.

Back to Json.
Here is a quick example from wiki:

This is a follow-up on XML. So we have to start with an XML example somehow 🙂

<person>
  <firstName>John</firstName>
  <lastName>Smith</lastName>
  <age>25</age>
  <address>
    <streetAddress>21 2nd Street</streetAddress>
    <city>New York</city>
    <state>NY</state>
    <postalCode>10021</postalCode>
  </address>
  <phoneNumbers>
    <phoneNumber type="home">212 555-1234</phoneNumber>
    <phoneNumber type="fax">646 555-4567</phoneNumber>
  </phoneNumbers>
</person>

or

<person firstName="John" lastName="Smith" age="25">
  <address streetAddress="21 2nd Street" city="New York" state="NY" postalCode="10021" />
  <phoneNumbers>
     <phoneNumber type="home" number="212 555-1234"/>
     <phoneNumber type="fax"  number="646 555-4567"/>
  </phoneNumbers>
</person>

And JSON looks more like C#, doesn’t it?
Btw. there is a really nice JSON online viewer here.

{
    "firstName": "John",
    "age": 25,
    "address": {
        "streetAddress": "21 2nd Street",
        "city": "New York",
        "state": "NY",
        "postalCode": "10021"
    },
    "phoneNumber": [
        {
            "type": "home",
            "number": "212 555-1234"
        },
        {
            "type": "fax",
            "number": "646 555-4567"
        }
    ]
}

First of all we need to make JavaScriptSerializer available. Go to your Solution Explorer and right-click your project, then select “Properties”. Change your “Target Framework” on the “Application” page to “.Net Framework 4” or higher. Do not select “.Net Framework 4 Client Profile“.

Settings

Add the reference “System.Web.Extensions” to your project. The namespace “System.Web.Script.Serialization” is available now.
(In case it is still in your AssemblyInfo.cs, remove [assembly: AllowPartiallyTrustedCallersAttribute])

Let’s now convert our “Walmart.xml” example to JSON. The example shows how to serialize objetcs. Nothing was changed in the class WalmartWorld (see earlier post).

public static void SaveObjectsJSON(WalmartWorld xWalmartWorld) {
    string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\";
    string lFile = lDesktopPath + "Walmart.json";

    JavaScriptSerializer lSerializer = new JavaScriptSerializer();
    string lSerialized = lSerializer.Serialize(xWalmartWorld);

    File.WriteAllText(lFile, lSerialized);
} //

Formatted example output:

{
  "Food": [
    {
      "Attribute": null,
      "Name": "Banana",
      "Price": 1.99,
      "Description": "Mexican delicious",
      "SomethingElse": null
    },
    {
      "Attribute": null,
      "Name": "Rice",
      "Price": 0.79,
      "Description": "the best you can get",
      "SomethingElse": null
    },
    {
      "Attribute": "What a wonderful world.",
      "Name": "Cornflakes",
      "Price": 3.85,
      "Description": "buy some milk",
      "SomethingElse": null
    },
    {
      "Attribute": null,
      "Name": "Milk",
      "Price": 1.43,
      "Description": "from happy cows",
      "SomethingElse": null
    },
    {
      "Attribute": null,
      "Name": "baked beans",
      "Price": 1.35,
      "Description": "very British",
      "SomethingElse": null
    }
  ],
  "Electronic": [
    {
      "Attribute": null,
      "Name": "Kindle fire",
      "Price": 100,
      "Description": "Amazon loves you",
      "SomethingElse": "the perfect Xmas gift for your kids"
    }
  ],
  "Text": "Luis Armstrong had a fabulous voice."
}
public static void LoadObjectJSON() {
    string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\";
    string lFile = lDesktopPath + "Walmart.json";

    JavaScriptSerializer lSerializer = new JavaScriptSerializer();
    string lSerialized = File.ReadAllText(lFile);
    WalmartWorld lWalmartWolrd = lSerializer.Deserialize<WalmartWorld>(lSerialized);

    foreach (WalmartItem lItem in lWalmartWolrd.Electronic) Console.WriteLine(lItem);
    foreach (WalmartItem lItem in lWalmartWolrd.Food) Console.WriteLine(lItem);
            
    Console.ReadLine();
} //

example output:

Kindle fire   100.00 Amazon loves you  !!! => the perfect Xmas gift for your kids
Banana          1.99 Mexican delicious
Rice            0.79 the best you can get
Cornflakes      3.85 buy some milk
Milk            1.43 from happy cows
baked beans     1.35 very British

Unfortunately we have the same mess as with XML. There are several classes dealing with JSON in the .Net framework.
There is another serializer in the System.Runtime.Serialization.Json namespace called DataContractJsonSerializer. It is intended for use with WCF client applications. It does make use of DataContractAttribute and DataMemberAttribute to identify the data to serialize. If you do not add any of these attributes, then data is ignored.
Add the “System.Runtime.Serialization” reference to your project.

Let’s extend our Walmart classes and add the required attributes. You can remove the XML attributes if you do not want to work with XML anymore. You can leave them in the code to keep the back door open for quick and easy XML conversion. The XML attributes do not affect the program, there are just some extra bytes.

BUT beware of a little typo. If you use DataContractSerializer instead of DataContractJsonSerializer, then you will accidentally create XML and not Json files. Such little problems in life can drive you nuts.

Further reading for DataContractAttribute and DataMemberAttribute.

[Serializable]
[DataContract(Name="WalmartWorld")]
[XmlRoot("Walmart", Namespace = "")]
public class WalmartWorld {
    [XmlElement("food")]
    [DataMember]
    public List<WalmartItem> Food { get; set; }

    [XmlElement("electronic")]
    [DataMember]
    public List<WalmartItem> Electronic { get; set; }

    [XmlText]
    [DataMember]
    public string Text { get; set; }
} // class

[Serializable]
[DataContract(Name = "WalmartItem")]
[XmlRoot("Walmart", Namespace = "")]
public class WalmartItem {
    [XmlAttribute("attr")]
    [DataMember(Name = "Attribute")]  // just to demonstrate that you could define a name
    public string Attribute { get; set; }

    [XmlElement("name")]
    [DataMember]
    public string Name { get; set; }

    [XmlElement("price")]
    [DataMember]
    public double Price { get; set; }

    [XmlElement("description")]
    [DataMember]
    public string Description { get; set; }

    [XmlElement("somethingElse")]
    [DataMember]
    public string SomethingElse { get; set; }

    public override string ToString() {
        return Name.PadRight(12) +
            Price.ToString("#,##0.00").PadLeft(8) + " " +
            Description +
            (string.IsNullOrEmpty(SomethingElse) ? string.Empty : ("  !!! => " + SomethingElse));
    } //
} // class

public static void SaveObjectsJSON2(WalmartWorld xWalmartWorld) {
    string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\";
    string lFile = lDesktopPath + "Walmart2.json";

    using (FileStream lFileStream = new FileStream(lFile, FileMode.OpenOrCreate)) {
        DataContractJsonSerializer lSerializer = new DataContractJsonSerializer(typeof(WalmartWorld));
        lSerializer.WriteObject(lFileStream, xWalmartWorld);
    }
} //

public static WalmartWorld LoadObjectJSON2() {
    string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\";
    string lFile = lDesktopPath + "Walmart.json";

    DataContractJsonSerializer lSerializer = new DataContractJsonSerializer(typeof(WalmartWorld));

    using (FileStream lFileStream = new FileStream(lFile, FileMode.Open)) {
        WalmartWorld lWalmartWolrd = lSerializer.ReadObject(lFileStream) as WalmartWorld;

        foreach (WalmartItem lItem in lWalmartWolrd.Electronic) Console.WriteLine(lItem);
        foreach (WalmartItem lItem in lWalmartWolrd.Food) Console.WriteLine(lItem);
                
        return lWalmartWolrd;
    }
} //

You could define your custom Name for each DataMember attribute. The JSON file would then address this name instead of the property name.

[DataMember(Name = "UseWhateverYouLike")]
public string Attribute { get; set; }
Advertisements

About Bastian M.K. Ohta

Happiness only real when shared.

Posted on January 23, 2014, in C#, Java, XML and tagged , , , , , , , , , , , , . Bookmark the permalink. Leave a comment.

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 )

Google+ photo

You are commenting using your Google+ 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 )

Connecting to %s

%d bloggers like this: