Reputation: 6841
I am attempt to use an API that use the follow example structure for their returned json
[
{
"customer":{
"first_name":"Test",
"last_name":"Account",
"email":"[email protected]",
"organization":"",
"reference":null,
"id":3545134,
"created_at":"2013-08-06T15:51:15-04:00",
"updated_at":"2013-08-06T15:51:15-04:00",
"address":"",
"address_2":"",
"city":"",
"state":"",
"zip":"",
"country":"",
"phone":""
}
},
{
"customer":{
"first_name":"Test",
"last_name":"Account2",
"email":"[email protected]",
"organization":"",
"reference":null,
"id":3570462,
"created_at":"2013-08-12T11:54:58-04:00",
"updated_at":"2013-08-12T11:54:58-04:00",
"address":"",
"address_2":"",
"city":"",
"state":"",
"zip":"",
"country":"",
"phone":""
}
}
]
JSON.net would work great with something like the following structure
{
"customer": {
["field1" : "value", etc...],
["field1" : "value", etc...],
}
}
But I can not figure out how to get it to be happy with the provided structure.
Using the default JsonConvert.DeserializeObject(content) results the correct number of Customer but all of the data is null.
Doing something a CustomerList (below) results in a "Cannot deserialize the current JSON array" exception
public class CustomerList
{
public List<Customer> customer { get; set; }
}
Thoughts?
Upvotes: 150
Views: 312210
Reputation: 20376
For those who don't want to create any models, use the following code:
var result = JsonConvert.DeserializeObject<
List<Dictionary<string,
Dictionary<string, string>>>>(content);
Note: This may not work for your JSON string. This is not a general solution for any JSON structure. It works for the JSON structure in the question. For your own JSON structure, you need to adjust the code.
Upvotes: 49
Reputation: 26274
Nowadays, you can use System.Text.Json and the JsonSerializer class. Either
CustomerList customers = JsonSerializer.Deserialize<CustomerList>(json);
or
List<Customer> customers = JsonSerializer.Deserialize<List<Customer>>(json);
The former is your own POCO class with a combination of properties which may be scalar or an arrays. The latter, for deserializing a specific array object.
Upvotes: 0
Reputation: 1
Simple: create two classes, one for customer and the other for array object.
Json string like this:
{
"fieldObjects": [
{
"Field1": "ValueField1_a",
"Field2": "ValueField2_a"
},
{
"Field1": "ValueField1_b",
"Field2": "ValueField2_b"
}
]
}
In .net you have the first class (parent) for fieldObjects that contains only one property list(of child class).
Child class contains properties Field1, Field2,...
Finally use DeserializeObject on Json string using parent class, without using the list of ...
That's all
Upvotes: -1
Reputation: 6499
You can create a new model to Deserialize your JSON CustomerJson
:
public class CustomerJson
{
[JsonProperty("customer")]
public Customer Customer { get; set; }
}
public class Customer
{
[JsonProperty("first_name")]
public string Firstname { get; set; }
[JsonProperty("last_name")]
public string Lastname { get; set; }
...
}
And you can deserialize your JSON easily:
JsonConvert.DeserializeObject<List<CustomerJson>>(json);
Documentation: Serializing and Deserializing JSON
Upvotes: 222
Reputation: 331
Further modification from JC_VA, take what he has, and replace the MyModelConverter with...
public class MyModelConverter : JsonConverter
{
//objectType is the type as specified for List<myModel> (i.e. myModel)
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader); //json from myModelList > model
var list = Activator.CreateInstance(objectType) as System.Collections.IList; // new list to return
var itemType = objectType.GenericTypeArguments[0]; // type of the list (myModel)
if (token.Type.ToString() == "Object") //Object
{
var child = token.Children();
var newObject = Activator.CreateInstance(itemType);
serializer.Populate(token.CreateReader(), newObject);
list.Add(newObject);
}
else //Array
{
foreach (var child in token.Children())
{
var newObject = Activator.CreateInstance(itemType);
serializer.Populate(child.CreateReader(), newObject);
list.Add(newObject);
}
}
return list;
}
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
This should work for json that is either
myModelList{
model: [{ ... object ... }]
}
or
myModelList{
model: { ... object ... }
}
they will both end up being parsed as if they were
myModelList{
model: [{ ... object ... }]
}
Upvotes: 0
Reputation: 27
Slight modification to what was stated above. My Json format, which validates was
{
mycollection:{[
{
property0:value,
property1:value,
},
{
property0:value,
property1:value,
}
]
}
}
Using AlexDev's response, I did this Looping each child, creating reader from it
public partial class myModel
{
public static List<myModel> FromJson(string json) => JsonConvert.DeserializeObject<myModelList>(json, Converter.Settings).model;
}
public class myModelList {
[JsonConverter(typeof(myModelConverter))]
public List<myModel> model { get; set; }
}
class myModelConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
var list = Activator.CreateInstance(objectType) as System.Collections.IList;
var itemType = objectType.GenericTypeArguments[0];
foreach (var child in token.Children()) //mod here
{
var newObject = Activator.CreateInstance(itemType);
serializer.Populate(child.CreateReader(), newObject); //mod here
list.Add(newObject);
}
return list;
}
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
Upvotes: 2
Reputation: 4727
Using the accepted answer you have to access each record by using Customers[i].customer
, and you need an extra CustomerJson
class, which is a little annoying. If you don't want to do that, you can use the following:
public class CustomerList
{
[JsonConverter(typeof(MyListConverter))]
public List<Customer> customer { get; set; }
}
Note that I'm using a List<>
, not an Array. Now create the following class:
class MyListConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
var list = Activator.CreateInstance(objectType) as System.Collections.IList;
var itemType = objectType.GenericTypeArguments[0];
foreach (var child in token.Values())
{
var childToken = child.Children().First();
var newObject = Activator.CreateInstance(itemType);
serializer.Populate(childToken.CreateReader(), newObject);
list.Add(newObject);
}
return list;
}
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType && (objectType.GetGenericTypeDefinition() == typeof(List<>));
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
Upvotes: 4