qqbenq qqbenq - 29 days ago 15
JSON Question

How to deserialize JSON string into a C# object?

Consider the following: You have a service which returns a JSON string, and in your C# code you would like to use the returned value as an object (e.g.: get the various properties of it by their names).

Sample JSON:

{
"Name": "Jon Smith",
"Address":
{
"City": "New York",
"State": "NY"
},
"Age": 42
}


Sample C# usage:

var object = ... // this is what I am asking for
Console.WriteLine(object.Name); // this should print out "Jon Smith"
Console.WriteLine(object.Address.State); // this should print out "NY"


What options are available in C# without using third party libraries?

Answer

If you don't know the exact data contract (or you don't care about it), then you can easily do that with the help of System.Web.Helpers.Json class and a dynamic object:

dynamic json = System.Web.Helpers.Json.Decode(@"{ ""Name"": ""Jon Smith"", ""Address"": { ""City"": ""New York"", ""State"": ""NY"" }, ""Age"": 42 }");
Console.WriteLine(json.Name);           // prints "Jon Smith"
Console.WriteLine(json.Address.State);  // prints "NY"

A note on that: you will have to add reference to the System.Web.Helpers assembly.

Of course not everyone likes dynamic, there are some people who prefer to have well defined data contracts. For them the solution is a little bit longer:

You have to create matching classes for your data contracts, and attribute them accordingly:

// class for the root object:
[DataContract]
public class Person
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public Address Address { get; set; }

    [DataMember]
    public int Age { get; set; }
}

// class for the address object:
[DataContract]
public class Address
{
    [DataMember]
    public string City { get; set; }

    [DataMember]
    public string State { get; set; }
}

You can mark members with IgnoreDataMember attribute to, well, ignore them, or add IsRequired=true to make them mandatory.

After defining those contracts, you can easily parse the JSON string into a Person object:

DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Person));
using( MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(@"{ ""Name"": ""Jon Smith"", ""Address"": { ""City"": ""New York"", ""State"": ""NY"" }, ""Age"": 42 }")))
{
    var person = (Person)ser.ReadObject(stream);
    Console.WriteLine(person.Name);             // prints "Jon Smith"
    Console.WriteLine(person.Address.State);    // prints "NY"
}

Note: the DataContractJsonSerializer resides in the System.ServiceModel.Web assembly, so you will have to add a reference for that. (And of course for the System.Runtime.Serialization assembly as well.)

To make it easier to use, you can add static Parse and TryParse methods to your data contract class:

public static Person Parse(string jsonString)
{
    if (String.IsNullOrWhiteSpace(jsonString)) throw new ArgumentNullException("The jsonString parameter shouldn't be null or an empty string.");

    DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Person));
    using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
    {
        return (Person)ser.ReadObject(stream);
    }
}

public static bool TryParse(string jsonString, out Person result)
{
    try
    {
        result = Person.Parse(jsonString);
        return true;
    }
    catch (Exception ex)
    {
        if (ex is ArgumentNullException || ex is SerializationException)
        {
            result = null;
            return false;
        }
        throw;
    }
}

As L.B. mentioned in their comment, you can use JavaScriptSerializer - if you add a reference to the System.Web.Extensions assembly - to make it even simpler:

var person = new JavaScriptSerializer().Deserialize<Person>(@"{ ""Name"": ""Jon Smith"", ""Address"": { ""City"": ""New York"", ""State"": ""NY"" }, ""Age"": 42 }");

For that you still need the classes from above, but you can leave out the - "ugly" - attributes if you would like to. (But with this method you lose the ability to mark parts as mandatory...)