Deserializing JSON Array into Strongly Typed .Net Object

Deserializing JSON array into strongly typed .NET object

Afer looking at the source, for WP7 Hammock doesn't actually use Json.Net for JSON parsing. Instead it uses it's own parser which doesn't cope with custom types very well.

If using Json.Net directly it is possible to deserialize to a strongly typed collection inside a wrapper object.

var response = @"
{
""data"": [
{
""name"": ""A Jones"",
""id"": ""500015763""
},
{
""name"": ""B Smith"",
""id"": ""504986213""
},
{
""name"": ""C Brown"",
""id"": ""509034361""
}
]
}
";

var des = (MyClass)Newtonsoft.Json.JsonConvert.DeserializeObject(response, typeof(MyClass));

return des.data.Count.ToString();

and with:

public class MyClass
{
public List<User> data { get; set; }
}

public class User
{
public string name { get; set; }
public string id { get; set; }
}

Having to create the extra object with the data property is annoying but that's a consequence of the way the JSON formatted object is constructed.

Documentation: Serializing and Deserializing JSON

Cannot deserialize JSON to strongly typed object

You should remove JsonArray attribute from SonarrSeries class and deserialize the json like this.

var s = JsonConvert.DeserializeObject<SonarrSeries[]>(jsonData)[0];
return s;

You don't need SonarrSeriesWrapper anymore by using this method.

How to deserialize a JSON array into an object using Json.Net?

Json.Net does not have a facility to automatically map an array into a class. To do so you need a custom JsonConverter. Here is a generic converter that should work for you. It uses a custom [JsonArrayIndex] attribute to identify which properties in the class correspond to which indexes in the array. This will allow you to easily update your model if the JSON changes. Also, you can safely omit properties from your class that you don't need, such as Filler.

Here is the code:

public class JsonArrayIndexAttribute : Attribute
{
public int Index { get; private set; }
public JsonArrayIndexAttribute(int index)
{
Index = index;
}
}

public class ArrayToObjectConverter<T> : JsonConverter where T : class, new()
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);

var propsByIndex = typeof(T).GetProperties()
.Where(p => p.CanRead && p.CanWrite && p.GetCustomAttribute<JsonArrayIndexAttribute>() != null)
.ToDictionary(p => p.GetCustomAttribute<JsonArrayIndexAttribute>().Index);

JObject obj = new JObject(array
.Select((jt, i) =>
{
PropertyInfo prop;
return propsByIndex.TryGetValue(i, out prop) ? new JProperty(prop.Name, jt) : null;
})
.Where(jp => jp != null)
);

T target = new T();
serializer.Populate(obj.CreateReader(), target);

return target;
}

public override bool CanWrite
{
get { return false; }
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}

To use the converter, you need to mark up your ChildModel class as shown below:

[JsonConverter(typeof(ArrayToObjectConverter<ChildModel>))]
class ChildModel
{
[JsonArrayIndex(0)]
public int ID { get; set; }
[JsonArrayIndex(1)]
public string StatusId { get; set; }
[JsonArrayIndex(2)]
public DateTime ContactDate { get; set; }
[JsonArrayIndex(3)]
public string State { get; set; }
[JsonArrayIndex(4)]
public string Status { get; set; }
[JsonArrayIndex(5)]
public string CustomerName { get; set; }
[JsonArrayIndex(6)]
public DateTime WorkStartDate { get; set; }
[JsonArrayIndex(7)]
public DateTime WorkEndDate { get; set; }
[JsonArrayIndex(8)]
public string Territory { get; set; }
[JsonArrayIndex(9)]
public string CustType { get; set; }
[JsonArrayIndex(10)]
public int JobOrder { get; set; }
[JsonArrayIndex(12)]
public string Link { get; set; }
}

Then just deserialize as usual and it should work as you wanted. Here is a demo: https://dotnetfiddle.net/n3oE3L

Note: I did not implement WriteJson, so if you serialize your model back to JSON, it will not serialize back to the array format; instead it will use the default object serialization.

How to deserialize an array of values with a fixed schema to a strongly typed data class?

The converter from Deserializing JSON in Visual Basic .NET should do what you need, suitably translated from VB.NET to c#:

public class ObjectToArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(T) == objectType;
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var objectType = value.GetType();
var contract = serializer.ContractResolver.ResolveContract(objectType) as JsonObjectContract;
if (contract == null)
throw new JsonSerializationException(string.Format("invalid type {0}.", objectType.FullName));
writer.WriteStartArray();
foreach (var property in SerializableProperties(contract))
{
var propertyValue = property.ValueProvider.GetValue(value);
if (property.Converter != null && property.Converter.CanWrite)
property.Converter.WriteJson(writer, propertyValue, serializer);
else
serializer.Serialize(writer, propertyValue);
}
writer.WriteEndArray();
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var contract = serializer.ContractResolver.ResolveContract(objectType) as JsonObjectContract;
if (contract == null)
throw new JsonSerializationException(string.Format("invalid type {0}.", objectType.FullName));

if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
return null;
if (reader.TokenType != JsonToken.StartArray)
throw new JsonSerializationException(string.Format("token {0} was not JsonToken.StartArray", reader.TokenType));

// Not implemented: JsonObjectContract.CreatorParameters, serialization callbacks,
existingValue = existingValue ?? contract.DefaultCreator();

using (var enumerator = SerializableProperties(contract).GetEnumerator())
{
while (true)
{
switch (reader.ReadToContentAndAssert().TokenType)
{
case JsonToken.EndArray:
return existingValue;

default:
if (!enumerator.MoveNext())
{
reader.Skip();
break;
}
var property = enumerator.Current;
object propertyValue;
// TODO:
// https://www.newtonsoft.com/json/help/html/Properties_T_Newtonsoft_Json_Serialization_JsonProperty.htm
// JsonProperty.ItemConverter, ItemIsReference, ItemReferenceLoopHandling, ItemTypeNameHandling, DefaultValue, DefaultValueHandling, ReferenceLoopHandling, Required, TypeNameHandling, ...
if (property.Converter != null && property.Converter.CanRead)
propertyValue = property.Converter.ReadJson(reader, property.PropertyType, property.ValueProvider.GetValue(existingValue), serializer);
else
propertyValue = serializer.Deserialize(reader, property.PropertyType);
property.ValueProvider.SetValue(existingValue, propertyValue);
break;
}
}
}
}

static IEnumerable<JsonProperty> SerializableProperties(JsonObjectContract contract)
{
return contract.Properties.Where(p => !p.Ignored && p.Readable && p.Writable);
}
}

public static partial class JsonExtensions
{
public static JsonReader ReadToContentAndAssert(this JsonReader reader)
{
return reader.ReadAndAssert().MoveToContentAndAssert();
}

public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}

public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}

Next, add the converter to your Player class, and indicate the order of each property using JsonPropertyAttribute.Order:

[JsonConverter(typeof(ObjectToArrayConverter<Player>))]
public class Player
{
[JsonProperty(Order = 1)]
public int UniqueID { get; set; }
[JsonProperty(Order = 2)]
public string PlayerDescription { get; set; }
// Other fields as required.
}

Then finally, declare your root object as follows:

public class ScoreboardResults
{
public int timestamp { get; set; }
public int total_players { get; set; }
public int max_score { get; set; }
public Dictionary<string, Player> players { get; set; }
}

Note that I have moved Username out of the Player class and into the dictionary, as a key.

Note that data contract attributes can be used instead of Newtonsoft attributes to specify order:

[JsonConverter(typeof(ObjectToArrayConverter<Player>))]
[DataContract]
public class Player
{
[DataMember(Order = 1)]
public int UniqueID { get; set; }
[DataMember(Order = 2)]
public string PlayerDescription { get; set; }
// Other fields as required.
}

Demo fiddles here, here and here.

Deserializing JSON search results into strongly typed lists (JSON.NET)

Json.NET will not do this without any customization. You have the following approaches:

  1. Post your classes to http://json2csharp.com/ to create a literal representation of your JSON, then use Linq to transform the result into a list of Book classes:

    public class Value
    {
    public string key { get; set; }
    public string value { get; set; } // Type changed from "object" to "string".
    }

    public class Result
    {
    public int resultnumber { get; set; }
    public List<Value> values { get; set; }
    }

    public class Searchresult
    {
    public int resultscount { get; set; }
    public List<Result> results { get; set; }
    }

    public class RootObject
    {
    public Searchresult searchresult { get; set; }
    }

    And then

        var root = JsonConvert.DeserializeObject<RootObject>(json);
    var books = root.searchresult.results.Select(result => new Book { Name = result.values.Find(v => v.key == "name").value, BookId = result.values.Find(v => v.key == "bookid").value });
  2. Create a custom JsonConverter to convert the JSON to your POCO as it is being read, for instance:

    internal class BookConverter : JsonConverter
    {
    public override bool CanWrite
    {
    get
    {
    return false;
    }
    }

    public override bool CanConvert(Type objectType)
    {
    return objectType == typeof(Book);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
    var values = serializer.Deserialize<List<KeyValuePair<string, string>>>(reader);
    if (values == null)
    return existingValue;
    var book = existingValue as Book;
    if (book == null)
    book = new Book();
    // The following throws an exception on missing keys. You could handle this differently if you prefer.
    book.BookId = values.Find(v => v.Key == "bookid").Value;
    book.Name = values.Find(v => v.Key == "name").Value;
    return book;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    throw new NotImplementedException();
    }
    }

    public class Result
    {
    public int resultnumber { get; set; }

    [JsonProperty("values")]
    [JsonConverter(typeof(BookConverter))]
    public Book Book { get; set; }
    }

    public class Searchresult
    {
    public int resultscount { get; set; }
    public List<Result> results { get; set; }
    }

    public class RootObject
    {
    public Searchresult searchresult { get; set; }
    }

    and then

        var root = JsonConvert.DeserializeObject<RootObject>(json);
    var books = root.searchresult.results.Select(result => result.Book);

    Here I only implemented ReadJson as your question only asks about deserialization . You could implement WriteJson similarly if required.

  3. Use Linq to JSON to load the JSON into a structured hierarchy of JObject's then convert the result to Book's with Linq:

        var books = 
    JObject.Parse(json).Descendants()
    .OfType<JProperty>()
    .Where(p => p.Name == "values")
    .Select(p => p.Value.ToObject<List<KeyValuePair<string, string>>>())
    .Select(values => new Book { Name = values.Find(v => v.Key == "name").Value, BookId = values.Find(v => v.Key == "bookid").Value })
    .ToList();

Deserialize json array of array to List of string in C#

You have not only string in your collection and you have nested arrays, so List<string> does not represent your JSON structure. If you want to get only string you can do something like this (this one is with Newtonsoft, after fixing the d value):

var strings = JsonConvert.DeserializeObject<List<List<object>>>(json)
.Select(arr => arr.OfType<string>().ToList())
.ToList();

Or using arr => arr.Select(a => a?.ToString() in Select if you want to convert all values to strings.

Or you can convert to List<JArray> with Newtonsoft and call ToString on it:

List<string> strings = JsonConvert.DeserializeObject<List<JArray>>(json)
.Select(jarr => jarr.ToString(Newtonsoft.Json.Formatting.None))
.ToList();
Console.WriteLine(string.Join(", ", strings)); // prints "["a","b","c",null,1], ["d","e",null,2]"

Cannot deserialize the current JSON object into type because the type requires a JSON array

you have several bugs in your code.Replace List<clsRaceTable> , List<clsCircuit> , List<clsLocation> with clsRaceTable,clsCircuit and clsLocation. Your classes should be

public class Root
{
public clsMRData MRData { get; set; }
}
public class clsMRData
{
public string xmlns { get; set; }
public string series { get; set; }
public string url { get; set; }
public string limit { get; set; }
public string offset { get; set; }
public string total { get; set; }
public clsRaceTable RaceTable { get; set; }
}

public class clsRaceTable
{
public string season { get; set; }
public List<clsRace> Races { get; set; }
}

public class clsRace
{
public string season { get; set; }
public string round { get; set; }
public string url { get; set; }
public string raceName { get; set; }
public clsCircuit Circuit { get; set; }
public string date { get; set; }
public string time { get; set; }
}

public class clsCircuit
{
public string circuitId { get; set; }
public string url { get; set; }
public string circuitName { get; set; }
public clsLocation Location { get; set; }
}

public class clsLocation
{
public string lat { get; set; }
[JsonProperty(PropertyName = "long")]
public string lon { get; set; }
public string locality { get; set; }
public string country { get; set; }
}

Deserializing JSON Object into C# list

You are trying to deserialize into a List

List<NewDocumentObject> newDoc = JsonConvert.DeserializeObject<List<NewDocumentObject>>(response.Content.ReadAsStringAsync().Result);

But your JSON string contains an object {} only.

Change your JSON input to [{...}]
Or change your deserialize call to only one object.



Related Topics



Leave a reply



Submit