Parse dynamic JSON - c#

I am using c# and json.net 9.0.1
I have the following json
{
"lineups": [
{
"55": {
"id": "55",
"game_id": "1",
"player_id": "55",
"jersey_number": "78"
},
"56": {
"id": "56",
"game_id": "1",
"player_id": "56",
"jersey_number": "77"
},
"57": {
"id": "57",
"game_id": "1",
"player_id": "57",
"jersey_number": "76"
}
}
]
}
All of the array items are of type Player. How can I parse the json so that each item "55", "56", "57" are stored in a list of Players, List?
The source json can't be modified as it is coming from a 3rd party.
UPDATE
Modified the json to be valid,

If you created two classes like this:
public class Lineup
{
public List<Dictionary<string,Player>> lineups;
}
public class Player
{
public string id {get; set; }
public string game_id { get; set;}
public string player_id {get;set;}
public string jersey_number {get;set;}
}
Then you should (after you've fixed your invalid JSON), be able to deserialize like this:
var l = Newtonsoft.Json.JsonConvert.DeserializeObject<Lineup>(source);
Example: https://dotnetfiddle.net/e7eUUZ
You can also use the various attributes in JSON.Net to customize the deserialization. For example, you might want to use the JsonPropertyAttribute to map the names in your JSON source to C# property names that match the standard.

Given these classes:
public class Lineup
{
public List<Player> Players { get; set; }
}
public class Player
{
public string id { get; set; }
public string game_id { get; set; }
public string player_id { get; set; }
public string jersey_number { get; set; }
}
public class Result
{
public List<Lineup> Lineups { get; set; }
}
You could implement a custom JsonConverter like this:
public class LineupConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Lineup);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
if (reader.TokenType != JsonToken.StartObject)
throw new Exception("Expected an object");
var jObject = (JObject)JObject.ReadFrom(reader);
// I suspect the property name are the same as the id property
// of the contained objects. Discarding the information from serialization
var players = jObject.Properties()
.Select(p => p.Value.ToObject<Player>());
return new Lineup
{
Players = players.ToList()
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then use:
var lineups = JsonConvert.DeserializeObject<Result>(json, new LineupConverter());
You could also annotate the Player properties with JsonProperty attributes and have them more C#-like.

Related

How to discard JSON elements with specific values while deserializing

This is an interesting situation. My application saves and loads JSON data from MongoDB, which works fine 95% of the time. The JSON data in such cases is like the following:
{
"isDemo": true,
"CustomerReference": "nabTest",
"Fee": null,
"OrderId": "48/XYZ3",
"Asynchronous": false,
}
The remaining 5% of the time,a legacy application loads some XML data, converts that into JSON format and inserts that into the same MongoDB collection. The JSON data in such cases is like this:
{
"#xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
"#xmlns:xsd":"http://www.w3.org/2001/XMLSchema",
"#xmlns":"http://MyDomain.com.au/SomeService.Contract/2007/06/18",
"isDemo":{
"#xmlns":"http://MyDomain.com.au/ABC.Services",
"#text":"true"
},
"Asynchronous":{
"#xmlns":"http://MyDomain.com.au/ABC.Services"
"#text":"false"
},
"Fee":{
"#xsi:nil":"true",
"#xmlns":"http://MyDomain.com.au/ABC.Services"
},
"CustomerReference":{
"#xmlns":"http://MyDomain.com.au/ABC.Services"
},
"OrderId":"48/XYZ3"
}
In such cases, Newtonsoft deserializer crashes with the following exception:
Newtonsoft.Json.JsonReaderException: 'Unexpected character encountered
while parsing value: {. Path 'CustomerReference', line 1, position
919.'
The minimum code for this is the following:
// 'result' is of type object, loaded from MongoDB
var resultStr = JsonConvert.SerializeObject(result);
var obj = JsonConvert.DeserializeObject<T>(resultStr, jsonSerializerSettings);
What is an elegant way to handle this situation, with minimal code changes.
Your Object representation for the normal Json should be something like this :
using J = Newtonsoft.Json.JsonPropertyAttribute;
public partial class Data
{
[J("isDemo")] public bool? IsDemo { get; set; }
[J("Asynchronous")] public bool? IsAsynchronous { get; set; }
[J("OrderId")] public string OrderId { get; set; }
[J("CustomerReference")] public string CustomerReference { get; set; }
[J("Fee")] public double? Fee { get; set; }
}
While having a custom JsonConverter handeling the whole Xml representation will be nice. I find it difficult to write and maintain.
You should divide and conquer:
Generate a class that is the representation of the Xml-ish Json.
Remove unnecessary property, keep Xml "nil" where needed
Create simple custom converter for mapping string representation to the correct type.
eg: "true" => bool, "123" => int,
public partial class XmlRepresentation
{
[J("isDemo")] public BoolWrapper IsDemo { get; set; }
[J("Asynchronous")] public BoolWrapper Asynchronous { get; set; }
[J("Fee")] public DoubleWrapper Fee { get; set; }
[J("CustomerReference")] public StringWrapper CustomerReference { get; set; }
[J("OrderId")] public string OrderId { get; set; }
}
public partial class NullableXmlValue
{
[J("#xsi:nil")][JsonConverter(typeof(BoolParseStringConverter))]
public bool IsNull { get; set; }
}
public partial class BoolWrapper :NullableXmlValue
{
[J("#text")][JsonConverter(typeof(BoolParseStringConverter))]
public bool Value { get; set; }
}
public partial class StringWrapper :NullableXmlValue
{
[J("#text")] public string Value { get; set; }
}
public partial class DoubleWrapper :NullableXmlValue
{
[J("#text")][JsonConverter(typeof(DoubleParseStringConverter))]
public double Value { get; set; }
}
With some simple String to Type {Bool, Double} parser:
internal class BoolParseStringConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(bool) || t == typeof(bool?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
if (Boolean.TryParse(value, out bool b))
{
return b;
}
throw new Exception("Cannot unmarshal type bool");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (bool)untypedValue;
var boolString = value ? "true" : "false";
serializer.Serialize(writer, boolString);
return;
}
public static readonly BoolParseStringConverter Singleton = new BoolParseStringConverter();
}
internal class DoubleParseStringConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(double) || t == typeof(double?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
if (Double.TryParse(value, out double l))
{
return l;
}
throw new Exception("Cannot unmarshal type long");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (double)untypedValue;
serializer.Serialize(writer, value.ToString());
return;
}
public static readonly DoubleParseStringConverter Singleton = new DoubleParseStringConverter();
}
This way you can directly deserialize to XmlRepresentation and use a simple projection logic to get the Data.
public partial class XmlRepresentation
{
public Data ToDataType(){
var result = new Data();
if( !this.IsDemo?.IsNull ?? false)
result.IsDemo = this.IsDemo.Value;
// etc..
return result;
}
}
And for knowing if it's the normal Json or the Xml-ish one I will simply use a try catch.
That way I won't have to write complexe JsonConverter or class that represent both Normal and Xml-ish.
Please use like this in Model:
public class User
{
// always require a string value
[JsonProperty("name", Required = Required.Always)]
public string Name { get; set; }
// don't require any value
[JsonProperty("role", NullValueHandling = NullValueHandling.Ignore)]
public string Role { get; set; }
// property is ignored
[JsonIgnore]
public string Password { get; set; }
}
Result generated with Json.NET serialization attributes:
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"role": {
"type": [
"string",
"null"
]
}
},
"required": [
"name"
]
}

Deserialize property to different class based on other property c#

I know there are many similar questions on SO, however all the ones I've found require a shared base class in order to work.
With a stream of JSON data like this:
[
{
"webhookType": "order",
"data": {
"id": "eeiefj393",
"orderProperty": "Value"
}
},
{
"webhookType": "customer",
"data": {
"id": 29238,
"customerProperty": "Value"
}
}
]
I wish to deserialize this into two containers, List<Customer> and List<Order>. Where the two classes are as follows:
class Order
{
public string Id { get; set; }
public string OrderProperty { get; set; }
[...]
}
class Customer
{
public long Id { get; set; }
public string CustomerProperty { get; set; }
[...]
}
There may be shared property names however there are no shared properties + type between these two classes and so the solutions involing a sub class aren't working for me.
You need to create a JsonConverter.
DataConverter
public class WebHookConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartObject)
{
JObject item = JObject.Load(reader);
if (item["webhookType"].Value<string>() == "order")
{
var webhook = new WebHook
{
Type = item["webhookType"].Value<string>(),
Data = item["data"].ToObject<Order>()
};
return webhook;
}
else if (item["webhookType"].Value<string>() == "customer")
{
var webhook = new WebHook
{
Type = item["webhookType"].Value<string>(),
Data = item["data"].ToObject<Customer>()
};
return webhook;
}
}
return null;
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
Objects
[JsonConverter(typeof(WebHookConverter))]
public class WebHook
{
[JsonProperty("webhookType")]
public string Type { get; set; }
public object Data { get; set; }
}
public class Order
{
public string Id { get; set; }
[JsonProperty("orderProperty")]
public string Property { get; set; }
}
public class Customer
{
public long Id { get; set; }
[JsonProperty("customerProperty")]
public string Property { get; set; }
}
Serialization
var json = File.ReadAllText("json1.json");
var obj = JsonConvert.DeserializeObject<List<WebHook>>(json);
var orderList = obj.Where(o => o.Type == "order").Select(o => o.Data).ToList();
var customerList = obj.Where(o => o.Type == "customer").Select(o => o.Data).ToList();
Output:

How to deserialize name-value collection pairs from a JSON string into an object?

I am trying to deserialize a JSON string into a strongly typed Payment class as defined below. The JSON contains some name-value pairs in the items collection. How can I deserialize these into the ID and PersonName properties on my class?
My JSON:
{
"status": 1,
"amount": 200.0,
"items": [
{
"name": "ID",
"value": "123456"
},
{
"name": "PersonName",
"value": "test"
}
]
}
My class:
public class Payment
{
[DataMember]
public string Status { get; set; }
[DataMember]
public string Amount { get; set; }
[DataMember]
public string ID { get; set; }
[DataMember]
public string PersonName { get; set; }
}
Here is how I am trying to deserialize it:
var response = JsonConvert.DeserializeObject<Payment>(message);
You will need to use a custom JsonConverter to do what you want. Below is a generic one which should do the job. The converter works by first loading the JSON into a JObject, then converterting the items array into regular properties on the JObject, and finally populating the target class from the JObject.
public class ClassWithItemsConverter<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)
{
JObject jo = JObject.Load(reader);
JProperty items = jo.Property("items");
if (items != null && items.Value.Type == JTokenType.Array)
{
foreach (JObject item in items.Value.Children<JObject>())
{
jo.Add((string)item["name"], item["value"]);
}
items.Remove();
}
T result = new T();
serializer.Populate(jo.CreateReader(), result);
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
You can use the converter by adding a [JsonConverter] attribute to your target class like this:
[JsonConverter(typeof(ClassWithItemsConverter<Payment>))]
public class Payment
{
...
}
Here is a working demo: https://dotnetfiddle.net/IuHT3O

Deserializing complex Json objects

I want to deserialize a complex and let's say not well constructed json. That code that I wrote doesn't deserialize the object, the MovieInfo property is null. You can find the example json in the code. I want to avoid using JObject.Parse and dynamic objects. What am I missing here?
using System.Collections.Generic;
using Newtonsoft.Json;
namespace ComplexJsonExample
{
class Program
{
static void Main(string[] args)
{
string jsonInText = #"
{
""movies"" : [
{
""Harry Potter"" : [
{ ""rating"": ""great""},
{ ""rating"": ""horrible""}
]
},
{
""Guardians of the galaxy"" : [
{ ""rating"": ""cool""},
{ ""rating"": ""awesome""}
]
}
]
}
";
var movieList = JsonConvert.DeserializeObject<MovieList>(jsonInText);
}
}
public class MovieList
{
[JsonProperty("movies")]
public IList<Movie> Movies { get; set; }
}
public class Movie
{
IDictionary<string, IList<MovieRating>> MovieInfo { get; set; }
}
public class MovieRating
{
[JsonProperty("rating")]
public string Rating { get; set; }
}
}
It's a bit ugly, but you can do it like this:
class MovieList
{
[JsonProperty("movies")]
public Movie[] Movies { get; set; }
}
class Movie : Dictionary<string, MovieRating[]>
{
}
class MovieRating
{
[JsonProperty("rating")]
public string Rating { get; set; }
}
It's weird that the movie is a dictionary with only one key (its title), but it matches the JSON structure. You can always map it to something more sensible after deserialization.
I consider this a bit of a hack, but it works, and deserializes your json string into the format you required:
class MovieConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Movie);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var movie = new Movie(){
MovieInfo = new Dictionary<string,IList<MovieRating>>()
};
while (reader.Read() && reader.Value != null)
{
var name = (string)reader.Value;
reader.Read();
var ratings = ((JArray)serializer.Deserialize(reader)).Values<string>("rating");
movie.MovieInfo.Add(name, ratings.Select(r => new MovieRating(){ Rating = r}).ToList());
}
return movie;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
I did have to make two changes to your original objects, first I made the accessor for MovieInfo public so I could access it, and second to add the JsonConverterAttribute:
[JsonConverter(typeof(MovieConverter))]
public class Movie
{
public IDictionary<string, IList<MovieRating>> MovieInfo { get; set; }
}

Modify the behaviour of JSON.NET to serialize a collection of objects to an array of IDs

I want to modify JSON.NET so that when I am serializing a Model from my API it sends only an array of IDs for a composite Collection object.
For example:
class Employee
{
public ICollection<Address> Addresses { get; set; }
}
class Address
{
public int id;
public string location;
public string postcode;
}
Then when I send that back through WebApi
Request.Createresponse(HttpStatusCode.OK, new Employee());
Instead of this:
{
"Addresses" :
[
{"id" : 1, "location" : "XX", "postcode" : "XX" },
{"id" : 2, "location" : "XX", "postcode" : "XX" }
]
}
It just sends as this:
{
"Addresss" : [1,2]
}
I want this to be happening application-wide and I don't want to modify at the specific place.
How can I achieve this using the JSON.NET serializer?
You can get the result you want using a custom JsonConverter such as this:
class IdOnlyListConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (typeof(IEnumerable).IsAssignableFrom(objectType) &&
objectType != typeof(string));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JArray array = new JArray();
foreach (object item in (IEnumerable)value)
{
PropertyInfo idProp = item.GetType().GetProperty("id");
if (idProp != null && idProp.CanRead)
{
array.Add(JToken.FromObject(idProp.GetValue(item, null)));
}
}
array.WriteTo(writer);
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
In your model, wherever you have a collection of something where you only want the IDs, decorate the collection property with a [JsonConverter] attribute specifying the custom converter. For example:
class Employee
{
public string name { get; set; }
[JsonConverter(typeof(IdOnlyListConverter))]
public ICollection<Address> Addresses { get; set; }
}
class Address
{
public int id { get; set; }
public string location { get; set; }
public string postcode { get; set; }
}
When the collection gets serialized, the converter will be used, and only the ID values will be written out. Demo:
class Program
{
static void Main(string[] args)
{
Employee emp = new Employee
{
name = "Joe",
Addresses = new List<Address>
{
new Address { id = 1, location = "foo", postcode = "bar" },
new Address { id = 2, location = "baz", postcode = "quux" }
}
};
string json = JsonConvert.SerializeObject(emp);
Console.WriteLine(json);
}
}
Output:
{"name":"Joe","Addresses":[1,2]}
Please have a look at Json.Net's documentation.
public class Address
{
[JsonProperty("Id")]
public int I'd { get; set; }
[JsonIgnore]
public string Location { get; set; }
[JsonIgnore]
public string PostalCode { get; set; }
}
The only drawback to this is that you'll never be able to serialize the Location and PostalCode properties to JSON should you ever want to.
I believe there is a way to specify the serialization that should be used when serializing to JSON with Json.Net. Again, have a look at their documentation.

Categories