I have a very undesirable situation which requires me to deserialize the JSON where the values are field names with JSON.NET. Assuming that I have the following JSON which is very properly structured:
{
"name": "tugberk",
"roles": [
{ "id": "1", "name": "admin" },
{ "id": "2", "name": "guest" }
]
}
It's very easy to deserialize this with JSON.NET to a CLR object:
class Program
{
static void Main(string[] args)
{
var camelCaseSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
var text = File.ReadAllText("user_normal.txt");
var obj = JsonConvert.DeserializeObject<User>(text, camelCaseSettings);
}
}
public class User
{
public string Name { get; set; }
public Role[] Roles { get; set; }
}
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
}
However, in my current case, I have the following horrible JSON which is equivalent to above JSON in terms of values:
{
"name": "tugberk",
"roles": {
"1": { "name": "admin" },
"2": { "name": "guest" }
}
}
As you can see, roles field is not an array; it's an object which contains other values as objects with it's unique keys as their field names (which is horrible). What's the best way to deserialize this JSON to above User class with JSON.NET?
You can create a custom JsonConverter which serializes/deserializes Role[]. You can then decorate your Roles property with the JsonConverterAttribute like this:
public class User
{
public string Name { get; set; }
[JsonConverter(typeof(RolesConverter))]
public Role[] Roles { get; set; }
}
In your converter class you are able to read an object and return an array instead. Your converter class may look like this:
class RolesConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Role[]);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// deserialize as object
var roles = serializer.Deserialize<JObject>(reader);
var result = new List<Role>();
// create an array out of the properties
foreach (JProperty property in roles.Properties())
{
var role = property.Value.ToObject<Role>();
role.Id = int.Parse(property.Name);
result.Add(role);
}
return result.ToArray();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
There are a few options available to you. You could have a custom JsonConverter and serialize it manually. Since by the time of writing this fero provided an answer based on this, I'll give you an alternative, requiring two surrogate classes:
public class JsonUser
{
public string Name { get; set; }
public Dictionary<int, JsonRole> Roles { get; set; }
}
public class JsonRole
{
public string Name { get; set; }
}
And in your Role class:
public static implicit operator User(JsonUser user)
{
return new User
{
Name = user.Name,
Roles = user.Roles
.Select(kvp => new Role { Id = kvp.Key, Name = kvp.Value.Name})
.ToArray()
};
}
Which can be used like this:
User jsonUser = JsonConvert.DeserializeObject<JsonUser>(json);
Now, this is done at the expense of creating an intermediate object and probably isn't suited for most cases.
For the sake of completeness, I'll include my version of the JsonConverter solution:
public class UserRolesConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof (Role[]);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<JObject>(reader)
.Properties()
.Select(p => new Role
{
Id = Int32.Parse(p.Name),
Name = (string) p.Value["name"]
})
.ToArray();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class User
{
public string Name { get; set; }
[JsonConverter(typeof(UserRolesConverter))]
public Role[] Roles { get; set; }
}
var jsonUser = JsonConvert.DeserializeObject<User>(json);
Hm you can try:
dynamic jObject = JObject.Parse(text);
List<User> users = new List<User>();
foreach(dynamic dUser in jObject)
{
List<Role> roles = new List<Role>();
User user = new User();
user.Name = dUser.name;
foreach(PropertyInfo info in dUser.GetType().GetProperties())
{
Role role = new Role();
role.Id = info.Name;
role.Name = dUser[info.Name].name;
roles.Ad(role);
}
user.Roles = roles.ToArray();
}
Related
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:
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.
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; }
}
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.
I am communicating with an API that returns JSON containing either true, false or an array of string arrays. I wish to deserialize this JSON and store the boolean value, if there is one, in a class field called Success of data type bool, and the array, if there is one, in a field called Result of a custom data type.
What is the best to go about achieving this?
Some JSON:
[
{"status":"ok","result":true},
{
"status":"ok",
"result":
[
{"name":"John Doe","UserIdentifier":"abc","MaxAccounts":"2"}
]
}
]
My Result class:
class Result
{
string Name,
string UserIdentifier,
string MaxAccounts
}
Class for communicating with the Api:
class Api
{
public string Status { get; set; }
public Result[] Result { get; set; }
public bool Success { get; set; }
}
With JSON.NET you could write a custom JSON converter. For example you could have the following objects:
public class Root
{
public string Status { get; set; }
public Result Result { get; set; }
}
public class Result
{
public bool? Value { get; set; }
public Item[] Items { get; set; }
}
public class Item
{
public string Name { get; set; }
public string UserIdentifier { get; set; }
public string MaxAccounts { get; set; }
}
and your JSON will be deserialized to a Root[].
Here's how the custom JSON converter may look like:
public class ResultConverter: JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Result);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Boolean)
{
return new Result
{
Value = (bool)reader.Value,
};
}
return new Result
{
Items = serializer.Deserialize<Item[]>(reader),
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// If you want to support serializing you could implement this method as well
throw new NotImplementedException();
}
}
and then:
class Program
{
static void Main()
{
var json =
#"[
{
""status"": ""ok"",
""result"": true
},
{
""status"": ""ok"",
""result"": [
{
""name"": ""John Doe"",
""UserIdentifier"": ""abc"",
""MaxAccounts"": ""2""
}
]
}
]";
var settings = new JsonSerializerSettings();
settings.Converters.Add(new ResultConverter());
Root[] root = JsonConvert.DeserializeObject<Root[]>(json, settings);
// do something with the results here
}
}
create and arraylist using Api and Results objects. I have just tried this and it works.
var api = new Api();
var result = new Result();
api.Status = "ok";
api.Success = true;
result.Name = "John Doe";
result.UserIdentifier = "abc";
result.MaxAccounts = "2";
api.Result = new Result[1];
api.Result[0] = result;
var arrayList = new ArrayList() { new {api.Status, api.Success},
new { api.Status, api.Result} };
var json = JsonConvert.SerializeObject(arrayList, Formatting.Indented);