I've been doing some json stuff in .Net (Json.Net) lately slightly beyond serializing and deserializing. It's basic stuff anyway, but it'll be useful (to me) to dump it here.
First, the basic distinction between the two main classes that we use when dealing with Json in .Net, JsonConvert and JObject. When we already have a .Net class that matches with the json string, we use JsonConvert.DeserializeObject<MyType>. If that's not the case, we use JObject.Parse to deserialize into a JObject, that with the power of dynamic provides us a very convenient way to read and manipulate that json structure. The only source of confusion is that JsonConvert.DeserializeObject<Object>(item), JsonConvert.DeserializeObject<dynamic>(item) and JsonConvert.DeserializeObject(item) will return a JObject, so it's equivalent to doing a JObject.Parse().
The casing difference between camelCase json and PascalCase properties in C# objects continues to make things a bit messy. Out of the box, when deserializing with JsonConvert.DeserializeObject
class PersonCamel { public string name {get;set;} public int age {get;set;} public override string ToString() { return this.name + " - " + this.age; } } class Person { public string Name {get;set;} public int Age {get;set;} public override string ToString() { return this.Name + " - " + this.Age; } } var jsonPerson = @"{ 'name':'Eric', 'age': 49 }"; personCamel = JsonConvert.DeserializeObject<PersonCamel>(jsonPerson); Console.WriteLine(personCamel.ToString()); //xuan - 45 person = JsonConvert.DeserializeObject<Person>(jsonPerson); Console.WriteLine(person.ToString()); //xuan - 45
When serializing our C# class to json, by default JsonConvert will use the name property as it is in our class. So if we want to avoid the pain of ending up with json strings in PascalCase, we'll have to help the serializer a bit with one of these 2 techniques:
JsonConvert.SerializeObject(person, new JsonSerializerSettings { ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() } }); JsonConvert.SerializeObject(person, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
Let's move now to more interesting stuff with JObject, dynamic and explicit/implicit conversions (I had already talked about this in this post). Let's see some code:
var jsonResult = @"{ 'result':'OK', 'users': [ { 'name':'xuan', 'age': 45 }, { 'name':'Francois', 'age': 47 } ] }"; var jsonPerson = @"{ 'name':'Eric', 'age': 49 }"; void TestJObjQuerying(string jsonResult, string jsonPerson) { Console.WriteLine("TestJObjQuerying"); dynamic resultJObj = JsonConvert.DeserializeObject(jsonResult); //to be able to use a linq query on the array we need to cast as JArray //then, as JArray implements IEnumerable of JToken we need to cast to dynamic again Console.WriteLine(String.Join(",", ((JArray)(resultJObj.users)).Select(it => (((dynamic)it).name)))); dynamic personJObj = JObject.Parse(jsonPerson); var name1 = personJObj.name; Console.WriteLine(name1.GetType().Name);//JValue string name2 = personJObj.name; //implicit conversion Console.WriteLine(name2.GetType().Name); //string var name3 = (string)(personJObj.name); //explicit conversion Console.WriteLine(name3.GetType().Name);//string FuncmyFunc = jObj => jObj.name; //implicit conversion for the return typle var name4 = myFunc(personJObj); Console.WriteLine(name4.GetType().Name);//string } TestJObjQuerying(jsonResult, jsonPerson);
We know that resultJObj.users is a JArray, but we need to hint the compiler with a cast, otherwise the compiler will consider it just as dynamic (the field of a dynamic object is a dynamic). JArray provides the Select method not as an own method, but as an Extension method available because it implements IEnumerable
The next lines in the method are a reminder of how implicit and explicit conversions make JObjects and dynamic work like magic.
A long while ago I already posted about a very interesting possibility, deserialize an object into a JObject and then take one part of that structure for which we have a .Net Type and convert it to that type by means of ToObject. In my old sample I was passing a serializer to ToObject because of the camel to Pascal, but now, same as I've just explained for JsonConvert, this is managed automatically.
dynamic obj = JObject.Parse(jsonResult); personJObject = obj.users[0]; var person = personJObject.ToObject<Person>(); //camel to Pascal works good automatically
The inverse procedure can also be useful. We have a .Net class and we want to serialize it to json with some additional fields. We'll use FromObject like this:
var person = new Person{ Name = "Jean", Age = 52 }; dynamic jPerson = JObject.FromObject(person); jPerson.City = "Marseille"; Console.WriteLine(jPerson.ToString()); //{ // "Name": "Jean", // "Age": 52, // "City": "Marseille" //}
The problem with the above is that there's not an out of the box way to serialize the JObject to camelCase json. You'll have to use some more elaborate technique as the one described here.
No comments:
Post a Comment