How to convert a JSON response to an object in C #?

18

I'm making a request and getting a JSON like this:

{
  "id": "1000000000000000", 
  "name": "BrunoLM", 
  "first_name": "Bruno", 
  "last_name": "X", 
  "link": "http://stackoverflow.com/users/340760/brunolm", 
  "username": "brunolm", 
  "bio": "...",
  "gender": "male", 
  "timezone": -2, 
  "locale": "en_US"
}

But the server does not always send all these fields, sometimes it can come without locale , or link ...

I would like to convert this to an object and be able to access it by properties:

objeto.name      // ou objeto.Name
objeto.last_name // ou objeto.LastName

How to do this? What is most recommended?

If the most recommended option is to use some library is a question of curiosity: is it possible to do using only what .NET offers?

    
asked by anonymous 20.12.2013 / 13:39

7 answers

18

I recommend using Json.NET

See the example Documentation

Product product = new Product();

product.Name = "Apple";
product.ExpiryDate = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };

string output = JsonConvert.SerializeObject(product);
//{
//  "Name": "Apple",
//  "ExpiryDate": "2008-12-28T00:00:00",
//  "Price": 3.99,
//  "Sizes": [
//    "Small",
//    "Medium",
//    "Large"
//  ]
//}

Product deserializedProduct = JsonConvert.DeserializeObject<Product>(output);
    
20.12.2013 / 13:49
5

To do this without using any external libraries, you can take advantage of the fact that JSON is a subset of JavaScript, and use JavaScriptSerializer from System.Web.Extensions.dll (.NET 3.5 SP1):

JavaScriptSerializer ser = new JavaScriptSerializer();
Dictionary dict = ser.Deserialize<Dictionary<string,object>>(input);

As the result is Dictionary , the lack of any field will not affect the result.

According to this answer in the English OS, it is also possible to define a class / class set to receive a result - instead of a Dictionary - so that you can access the properties the way you prefer. I just do not know how he would behave with missing fields, would have to do a test.

Example code for the above question:

using System.Collections.Generic;
using System.Web.Script.Serialization;
public class NameTypePair
{
    public string OBJECT_NAME { get; set; }
    public string OBJECT_TYPE { get; set; }
}
public enum PositionType { none, point }
public class Ref
{
    public int id { get; set; }
}
public class SubObject
{
    public NameTypePair attributes { get; set; }
    public Position position { get; set; }
}
public class Position
{
    public int x { get; set; }
    public int y { get; set; }
}
public class Foo
{
    public Foo() { objects = new List<SubObject>(); }
    public string displayFieldName { get; set; }
    public NameTypePair fieldAliases { get; set; }
    public PositionType positionType { get; set; }
    public Ref reference { get; set; }
    public List<SubObject> objects { get; set; }
}
static class Program
{

    const string json = @"{
  ""displayFieldName"" : ""OBJECT_NAME"", 
  ""fieldAliases"" : {
    ""OBJECT_NAME"" : ""OBJECT_NAME"", 
    ""OBJECT_TYPE"" : ""OBJECT_TYPE""
  }, 
  ""positionType"" : ""point"", 
  ""reference"" : {
    ""id"" : 1111
  }, 
  ""objects"" : [
    {
      ""attributes"" : {
        ""OBJECT_NAME"" : ""test name"", 
        ""OBJECT_TYPE"" : ""test type""
      }, 
      ""position"" : 
      {
        ""x"" : 5, 
        ""y"" : 7
      }
    }
  ]
}";

    static void Main()
    {
        JavaScriptSerializer ser = new JavaScriptSerializer();
        Foo foo = ser.Deserialize<Foo>(json);
    }
}
    
20.12.2013 / 13:49
4

You can use the Json.Decode method ( part of ASP.NET MVC) to get a dynamic object with JSON properties:

dynamic pessoa = Json.Decode(json);

Console.WriteLine(pessoa.name);

And if you have a class with the same JSON properties, you can use Json.Decode<T> to get an instance of the desired object:

Pessoa pessoa = Json.Decode<Pessoa>(json);

Console.WriteLine(pessoa.name);
    
20.12.2013 / 17:13
4

Fluent URL Http

There is a component called Fluent URL Http to do all this task of performing a request and receive an already deserialized object.

First install the package in your project using NuGet Package Flurl .

  

PM > Install-Package Flurl.Http

Then, just consume and make requests in the most elegant way I've ever seen:

var result = await "https://api.mysite.com"
    .AppendPathSegment("person") // https://api.mysite.com/person
    .SetQueryParams(new { a = 1, b = 2 }) // https://api.mysite.com/person?a=1&b=2
    .WithOAuthBearerToken("my_oauth_token")
    .PostJsonAsync(new { first_name = "Frank", last_name = "Underwood" }) // { "first_name": "Frank", "last_name": "Underwook" }
    .ReceiveJson<T>();
    
19.08.2016 / 14:43
1

DataContractJsonSerializer

There is also DataContractJsonSerializer , which requires the properties of a class to have the [DataMember] attribute.

This class can be serialized with DataContractJsonSerializer , but the ID property will not be included.

[DataContract]
public class TestClass
{
    public int ID { get; set; }

    [DataMember(Name = "número")]
    public int Number { get; set; }
}

This way you can use

var js = new DataContractJsonSerializer(typeof(TestClass));

using (var ms = new MemoryStream())
{
    js.WriteObject(ms, new TestClass { ID = 1, Number = 55 });

    // {"número":55}
    string jsonSerializado = Encoding.UTF8.GetString(ms.ToArray());

    ms.Position = 0;

    // new TestClass { ID = 0, Number = 55 }
    var jsonDeserealizado = (TestClass)js.ReadObject(ms);
}
    
20.12.2013 / 20:26
0

Using dynamic

It is possible to extend a DynamicObject to get the values as follows:

public class DynamicJsonObject : DynamicObject
{
    private IDictionary<string, object> Dictionary { get; set; }

    public DynamicJsonObject(IDictionary<string, object> dictionary)
    {
        this.Dictionary = dictionary;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = this.Dictionary[binder.Name];

        if (result is IDictionary<string, object>)
        {
            result = new DynamicJsonObject(result as IDictionary<string, object>);
        }
        else if (result is ArrayList && (result as ArrayList) is IDictionary<string, object>)
        {
            result = new List<DynamicJsonObject>((result as ArrayList).ToArray().Select(x => new DynamicJsonObject(x as IDictionary<string, object>)));
        }
        else if (result is ArrayList)
        {
            result = new List<object>((result as ArrayList).ToArray());
        }

        // nunca lança exceção se não encontrar o valor
        return true;
        // this.Dictionary.ContainsKey(binder.Name);
    }
}

And extend JavaScriptConverter to get values

public class DynamicJsonConverter : JavaScriptConverter
{
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        if (dictionary == null)
            throw new ArgumentNullException("dictionary");

        if (type == typeof(object))
        {
            return new DynamicJsonObject(dictionary);
        }

        return null;
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get { return new ReadOnlyCollection<Type>(new List<Type>(new Type[] { typeof(object) })); }
    }
}

And to use

JavaScriptSerializer jss = new JavaScriptSerializer();
jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });

dynamic json = jss.Deserialize(jsonString, typeof(object));

This way you can dynamically access object values.

json.name
json.id
// ...

You could modify the method TrySetMember of DynamicJsonObject so that you could modify the values of the object. It is shown as read only.

    
20.12.2013 / 20:25
-2
return Json(new { id= "1000000000000000", name= "BrunoLM", first_name= "Bruno", last_name= "X", link= "http://stackoverflow.com/users/340760/brunolm", username= "brunolm", bio= "...", gender= "male", timezone= -2, locale "en_US" });
    
03.03.2015 / 12:55