0

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?

1 Answer 1

5

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...)

Sign up to request clarification or add additional context in comments.

7 Comments

Were you supposed to answer this under a different account? All looks a bit odd at the moment what with question and (very long) answer being within 1 minute of each other by the same user.
There is an option here to answer your own question... They get posted at the same time. I find it a quite good way to document problems you face, and have a way to find them later.
@qqbenq If you used JavaScriptSerializer, you wouldn't need those ugly attributes. A one line deserialization is also possible. new JavaScriptSerializer().Deserialize<Person>(jsonstring)
@L.B Thanks, I incorporated that in my answer, but that method also has some drawbacks: you lose the ability to mark fields as mandatory, if I'm not mistaken.
@qqbenq What do you mean by mandatory, if that field doesn't exist in json, you'll get the default value. BTW: You can use ScriptIgnore attribute if needed.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.