How does JSON deserialization in C# work
After digging into Newtonsoft.Json sources I can tell you algorithm of object instantiation which is used there. And yes, constructor is almost always called (*). The question is only "which one?". Here is colorful version of answer:
TL;DR First of all, Newtonsoft.Json creates JsonContract
of the type which you are going to deserialize. Its abstract class. And it has different implementations for dictionaries, arrays, objects etc. In your case JsonObjectContract
will be created. Contract contains various metadata about deserialized type. Most interesting for us are:
IsInstantiable
- defines whether deserialized type is instantiable (see below)Properties
- its collection of object propertiesDefaultCreator
- default creation method used to create objectFunc<object>
DefaultCreatorNonPublic
- defines whether default constructor is non publicOverrideCreator
- non-default creator, used ifJsonConstructorAttribute
is applied to object's constructorParametrizedCreator
- creator which calls paramterized constructor, it is used if we don't have neither default nor override creatorsCreatorParameters
- collection of properties which are used for override creator or parametrized creatorMemberSerialization
- this value defines the way how properties and fields are serialized. By default it is set toOptOut
- i.e. all public members are serialized. If you want to exclude some, you should useJsonIgnore
attribute. But there is alsoFields
option, which says that all public and private fields should be serialized. There is several to turn-on this option. But by default its disabled.
Some of this metadata can be retrieved by reflecting type metdata. E.g. IsInstantiable
is calculated by checking whether deserialized type is not abstract and not interface. Some metadata are added by DefaultContractResolver
. In particular, it defines the way how object should be constructed. In pseudo-code:
if (contract.IsInstantiable)
{
if (type has default constructor or its a value type)
{
contract.DefaultCreator = get default (parameterless) constructor;
contract.DefaultCreatorNonPublic = check if default constructor public
}
if (we have constructor marked with JsonConstructorAttribute)
{
contract.OverrideCreator = constructor marked with attribute
contract.CreatorParameters = get properties which match constructor parameters
}
else if (contract.MemberSerialization == MemberSerialization.Fields)
{
// only if the upplication if fully trusted
contract.DefaultCreator = FormatterServices.GetUninitializedObject
}
else if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic)
{
if (we have one public constructor with parameters)
{
contract.ParametrizedCreator = constructor with parameters;
contract.CreatorParameters = get properties which match ctor parameters
}
}
}
So, as you can see priority goes to constructor marked with JsonConstructorAttribute
attribute. You will also get error if there is more than one such constructor.
(*) Next goes the only case when object can be created without calling constructor. E.g. if you'll mark class with [JsonObject(MemberSerialization = MemberSerialization.Fields)]
attribute to serialize private fields.
Then we check if we have default parameterless constructor which is not private. If so, then we go for other constructor - one which has parameters and should be public. If there is more than one such constructor, you will also get error.
And last thing to note - CreatorParameters
. Newtonsoft.Json uses reflection to get constructor parameters and then tries to find closest match by name of these constructor parameters to object's properties. It also checks type of property and parameters to match. If there is no match found, then default value will be passed to this parameterized constructor.
Deserialize JSON with C#
You need to create a structure like this:
public class Friends
{
public List<FacebookFriend> data {get; set;}
}
public class FacebookFriend
{
public string id {get; set;}
public string name {get; set;}
}
Then you should be able to do:
Friends facebookFriends = new JavaScriptSerializer().Deserialize<Friends>(result);
The names of my classes are just an example. You should use proper names.
Adding a sample test:
string json =
@"{""data"":[{""id"":""518523721"",""name"":""ftyft""}, {""id"":""527032438"",""name"":""ftyftyf""}, {""id"":""527572047"",""name"":""ftgft""}, {""id"":""531141884"",""name"":""ftftft""}]}";
Friends facebookFriends = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Friends>(json);
foreach(var item in facebookFriends.data)
{
Console.WriteLine("id: {0}, name: {1}", item.id, item.name);
}
Produces:
id: 518523721, name: ftyft
id: 527032438, name: ftyftyf
id: 527572047, name: ftgft
id: 531141884, name: ftftft
How to Deserialize JSON data?
You can deserialize this really easily. The data's structure in C# is just List<string[]>
so you could just do;
List<string[]> data = JsonConvert.DeserializeObject<List<string[]>>(jsonString);
The above code is assuming you're using json.NET.
EDIT: Note the json is technically an array of string arrays. I prefer to use List<string[]>
for my own declaration because it's imo more intuitive. It won't cause any problems for json.NET, if you want it to be an array of string arrays then you need to change the type to (I think) string[][]
but there are some funny little gotcha's with jagged and 2D arrays in C# that I don't really know about so I just don't bother dealing with it here.
Why does Newtonsoft JsonConvert not deserialize Json array fields?
The reason why you could not deserialize it directly is because your json and your data model has a mismatch.
"details":"[{\"field_id\":\"1142488407\",\"response\":\"256\",\"field_type\":\"text\"},..]"
- In this case your
details
data type is string - So, you can NOT parse it directly as a collection of objects
if your json would look like this:
"details":[{\"field_id\":\"1142488407\",\"response\":\"256\",\"field_type\":\"text\"},..]
- the the data type of the
details
field would be a collection
So, the very reason why did your second approach worked is because you have first deserialized the details
then you deserialized the json array.
Deserializing and working with a JSON object
First you need to create a class to work with the returned objects (assuming you know what you are getting ahead of time).
public class ShoppingbasketItem
{
public double price { get; set; }
public string product { get; set; }
public int quantity { get; set; }
public int total { get; set; }
}
public class Shoppingbasket
{
public List<ShoppingbasketItem> shoppingbasket { get; set; }
}
Then you can deserialize the objects into your C# code and then do whatever you would need to with them from there.
var products = Newtonsoft.Json.JsonConvert.DeserializeObject<Shoppingbasket>(Request.Cookies.Get("wrkb").Value);
So to compute a sum of the total you could just iterate the list from the basket.
double totalSum = 0.0;
foreach(var item in products.shoppingbasket)
{
totalSum += item.quantity * item.price;
}
Edit
To achieve the grouping you are looking for, you could do something like this. It is probably not the best solution, but off the top of my head it is what I thought of.
var grouped = new Dictionary<string, ShoppingbasketItem>();
foreach (var item in products.shoppingbasket)
{
ShoppingbasketItem temp = null;
if (grouped.TryGetValue(item.product, out temp))
{
temp.quantity += item.quantity;
temp.total += item.total;
}
else
{
grouped[item.product] = item;
}
}
Related Topics
Avoiding First Chance Exception Messages When the Exception Is Safely Handled
List<T> Orderby Alphabetical Order
How to Run a Test Method with Multiple Parameters in Mstest
Why Is Httpcontext.Current Null After Await
How to Run Console Application from Windows Service
Why Use Simple Properties Instead of Fields in C#
Why Switch for Enum Accepts Implicit Conversion to 0 But No for Any Other Integer
How to Use Mvvmlight Simpleioc
Listen to Changes of Dependency Property
Advantages of Cache VS Session
Converting Bitmap Pixelformats in C#
Synchronously Waiting for an Async Operation, and Why Does Wait() Freeze the Program Here
Preprocessor Directives in Razor
Get the Value of Displayname Attribute
How to Deserialize a Complex JSON Object in C# .Net
Property(With No Extra Processing) VS Public Field
Embedding One Dll Inside Another as an Embedded Resource and Then Calling It from My Code