Using JSON.NET I am stuck on a issue with MasterData with regards to GET and POST operations.
Using the example of a movie, the JSON I could get on a GET operation before deserializing is the following:
{
“movie”: {
“name”:”bad boys”,
”genre”: {
“id”:"1",
”name”:”thriller”
}
}
}
However, on a POST operation I would like to construct a serialized string like the following:
{
“movie”: {
“name”:”bad boys”,
”genre”:"1"
}
}
As you can see, with a GET operation I need to deserialize the complete object, while on serialize for POST I need to add the Id only, but with the same JsonPropertyName.
What is the cleanest way to obtain this situation? I've been trying to work on the IContractResolver and IReferenceResolver, but those both didn't get me there yet. Also have been trying to work with propertynames as the following example:
[JsonProperty(PropertyName = "genre")]
public Genre Genre { get; set; }
[JsonProperty(PropertyName = "genre")]
public string GenreId { get { return Genre != null ? Genre.Id : 0; } }
I've also tried using the ShouldSerialize[MemberName] and ShouldDeserialize[MemberName] situations, but those gave an exception saying I cannot use two properties with the same name, which is logical in a way.
For now, my only idea left is to use a DTO for serialization, but I would rather prefer something more clean than that solution. So open for suggestions!
You can write a Custom converter.
public class GenreConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Genre);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JObject.Load(reader).ToObject<Genre>();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var genre = value as Genre;
writer.WriteValue(genre.id);
}
}
All you need now is using JsonConverter atttribute
public class Movie
{
public string Name { get; set; }
[JsonConverter(typeof(GenreConverter))]
public Genre Genre { get; set; }
}
Use custom converter for this. You want to check type of genre attribute and read it as object or int.
In my opinion, using a DTO is the cleanest solution. Since your models are different for GET/POST, you ideally require two separate models. I've abused the NullValueHandling.Ignore serializer setting to mean you can use the same model for both situations, providing you don't mind always having the Id property on Genre:
class MovieJsonWrapper
{
[JsonProperty("movie")]
public Movie Movie { get; set; }
}
class Movie
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("genre")]
public Genre Genre { get; set; }
}
class Genre
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
We can then use this with:
var movie = new Movie
{
Name = "bad boys",
Genre = new Genre
{
Id = "1"
}
};
var movieJsonWrapper = new MovieJsonWrapper { Movie = movie };
var jsonSerializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
var json = JsonConvert.SerializeObject(movieJsonWrapper, jsonSerializerSettings);
Which would produce the following:
{
"movie": {
"name": "bad boys",
"genre": {
"id": "1"
}
}
}
Similarly, we can then deserialize the GET response with:
var result = JsonConvert.DeserializeObject<MovieJsonWrapper>(raw);
If you really do need to ditch the Id property on the Genre when POSTing, then you would require additional Models. This solution reduces the amount of boilerplate.
Related
From an external API I am receiving the below JSON response for the bank details of a customer.
{
"bankDetails":[
{
"ABC Bank":[
{
"sNo":1,
"acNo":"1235465",
"acBalance":"100.25"
},
{
"sNo":2,
"acNo":"1235467",
"acBalance":"50.25"
}
],
"bankName":"ABC Bank",
"totalAmount":"150.50"
},
{
"XYZ Bank":[
{
"sNo":1,
"acNo":"1248565",
"acBalance":"75.25"
}
],
"bankName":"XYZ Bank",
"totalAmount":"75.25"
},
{
"BCD Bank":[
{
"sNo":1,
"acNo":"145665",
"acBalance":"10.25"
},
{
"sNo":2,
"acNo":"195267",
"acBalance":"5.25"
}
],
"bankName":"BCD Bank",
"totalAmount":"15.50"
}
]
}
I need to deserialize this to a C# class using JSON.Net. What should be structure of the C# class as the first key is dynamic?. The first key with bank name returned will be different for each customer
The typical solution to dealing with dynamic keys is to use a Dictionary<string, T> in place of a regular class. See How can I deserialize a child object with dynamic (numeric) key names? for an example of this. However, that solution doesn't really work for your case, because there are other properties in the same object which do not have dynamic keys (the bankName and totalAmount), and the values of those properties are primitives whereas the value of dynamic property is an array of bank accounts. A better solution here is to use a JsonConverter.
Before we get to that, we need to set up a class structure to deserialize into. This is pretty straightforward:
class RootObject
{
public List<Bank> BankDetails { get; set; }
}
[JsonConverter(typeof(BankConverter))]
class Bank
{
public string BankName { get; set; }
public decimal TotalAmount { get; set; }
public List<Account> Accounts { get; set; }
}
class Account
{
[JsonProperty("sNo")]
public int SequenceNumber { get; set; }
[JsonProperty("acNo")]
public string AccountNumber { get; set; }
[JsonProperty("acBalance")]
public decimal Balance { get; set; }
}
You'll notice that I've added a few [JsonProperty] attributes in the Account class to map the shorthand property names in the JSON to friendlier property names in that class. And the [JsonConverter] attribute on the Bank class tells the serializer that we will be using a custom BankConverter to handle that class.
Here is the code for the BankConverter. It uses a JObject internally to make it easier to read and work with the JSON.
class BankConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Bank);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
Bank bank = new Bank();
// populate the known properties (bankName and totalAmount)
serializer.Populate(obj.CreateReader(), bank);
// now handle the dynamic key
bank.Accounts = obj[bank.BankName].ToObject<List<Account>>(serializer);
return bank;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
With these classes in place, you can deserialize the JSON like this:
var root = JsonConvert.DeserializeObject<RootObject>(json);
Here is a working demo: https://dotnetfiddle.net/knsRLv
Given the following 2 classes (edited for brevity), I am generating a list of questions. Each question is assigned a 'UserAccount'.
I then want to serialise the list of questions, but for each UserAccount, I only want to write the 'Id' property (exclude any other properties on the UserAccount object).
I think I need a custom converter to perform this, but am unsure how to go about intercepting any occurrence of the UserAccount property, and only serialise the Id property on it.
public class Question
{
public int Id { get; set; }
public string Ask { get; set; }
public UserAccount CreatedBy { get; set; }
}
public class UserAccount
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
This is what the resulting JSON would ideally look like:
[
{
"Id":1,
"Ask":"Question 1",
"CreatedBy":{
"Id":1
}
},
{
"Id":2,
"Ask":"Question 2",
"CreatedBy":{
"Id":1
}
},
{
"Id":3,
"Ask":"Question 3",
"CreatedBy":{
"Id":1
}
}
]
I don't want to use Ignore attributes against the UserAccount class, as in some other business cases, I might want all of the attributes serialised. In fact, I don't want to have to modify either the Question or UserAccount class.
You want to ignore the other properties conditionally. To achieve this, using NewtonJson you can simply add a method to your class next to the properties which has the following signiture (bool ShouldSerialize[PropertyName]), see the official documentation for more details.
So your code will look like this
public class UserAccount
{
// This will be serialized
public int Id { get; set; }
// This may (or may not) be be serialized depending on your condition
public string Name { get; set; }
// This also may (or may not) be be serialized depending on your condition
public string Email { get; set; }
public bool ShouldSerializeName()
{
if(someCondition)
{
return true;
}
else
{
return false;
}
}
public bool ShouldSerializeEmail()
{
if(someCondition)
{
return true;
}
else
{
return false;
}
}
}
If you don't want to edit your original classes, you may for example wrap them inside another class (or inherit from them), and use the derrived class. So you just inherit the properties and add the methods to the derrived class. This could be one possible solution (I am not sure if you might get a better solution).
if you are using NewtonSoft in order to do that you have to add "JsonIgnore" attribute to the prop(or "DataContract" to the class and "DataMember" to the prop) or implement IContractResolver
https://www.newtonsoft.com/json/help/html/ReducingSerializedJSONSize.htm
You can create a custom JsonConverter for your requirement and within the 'WriteJson' method, you can try the following implementation:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject o = (JObject)t;
var property = o.Properties().FirstOrDefault(p => p.Name == "CreatedBy");
if(property != null)
{
o.Remove(property.Name);
var newObj = new JObject();
newObj.Add(new JProperty("Id",((JObject)property.Value).Properties().First(p => p.Name == "Id").Value));
var newProperty = new JProperty(property.Name, newObj);
o.Add(newProperty);
o.WriteTo(writer);
}
}
}
And the serialization code would be like this:
var json = JsonConvert.SerializeObject(questions, // your list of 'Question'
Newtonsoft.Json.Formatting.Indented,
new MyCustomJsonConverter(typeof(Question)));
Does Newtonsoft.JSON library have a simple way I can automatically deserialize JSON into 2 different Models/classes?
For example I get the JSON:
[{
"guardian_id": "1453",
"guardian_name": "Foo Bar",
"patient_id": "938",
"patient_name": "Foo Bar",
}]
And I need de-serialize this to the following models:
class Guardian {
[JsonProperty(PropertyName = "guardian_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "guardian_name")]
public int Name { get; set; }
}
class Patient {
[JsonProperty(PropertyName = "patient_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "patient_name")]
public int Name { get; set; }
}
Is there a simple way to deserialize this JSON into 2 Models without having to iterate over the JSON? Maybe JSON property ids will just work?
Pair<Guardian, Patient> pair = JsonConvert.DeserializeObject(response.Content);
First off, your models are slightly incorrect. The name properties need to be strings, instead of integers:
class Guardian
{
[JsonProperty(PropertyName = "guardian_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "guardian_name")]
public string Name { get; set; } // <-- This
}
class Patient
{
[JsonProperty(PropertyName = "patient_id")]
public int ID { get; set; }
[JsonProperty(PropertyName = "patient_name")]
public string Name { get; set; } // <-- This
}
Once you've corrected that, you can deserialize the JSON string into two lists of different types. In your case, List<Guardian> and List<Patient> respectively:
string json = #"[{'guardian_id':'1453','guardian_name':'Foo Bar','patient_id':'938','patient_name':'Foo Bar'}]";
var guardians = JsonConvert.DeserializeObject<List<Guardian>>(json);
var patients = JsonConvert.DeserializeObject<List<Patient>>(json);
It you want to do it with 1 call, you need to create a class that matches the JSON. That class can then return Guardian and Patient objects as needed. Also you'll need to use an array or list for the return type because the source JSON is an array.
The class to create:
public class Pair
{
public Pair()
{
Guardian = new Guardian();
Patient = new Patient();
}
[JsonIgnore]
public Guardian Guardian { get; set; }
[JsonIgnore]
public Patient Patient { get; set; }
[JsonProperty(PropertyName = "guardian_id")]
public int GuardianID
{
get { return Guardian.ID; }
set { Guardian.ID = value; }
}
[JsonProperty(PropertyName = "guardian_name")]
public string GuardianName
{
get { return Guardian.Name; }
set { Guardian.Name = value; }
}
[JsonProperty(PropertyName = "patient_id")]
public int PatientID
{
get { return Patient.ID; }
set { Patient.ID = value; }
}
[JsonProperty(PropertyName = "patient_name")]
public string PatientName
{
get { return Patient.Name; }
set { Patient.Name = value; }
}
}
And how to use it:
var pairs = JsonConvert.DeserializeObject<Pair[]>(response.Content);
if (pairs.Any())
{
var pair = pairs[0];
Console.WriteLine(pair.Guardian.Name);
Console.WriteLine(pair.Patient.Name);
}
Not in one call, and it seems the data is an array, so you need a little more work.
Zip is the key method here to join the two separate object lists:
Guardian[] guardians = JsonConvert.DeserializeObject<Guardian[]>(response.Content);
Patient[] patients = JsonConvert.DeserializeObject<Patient[]>(response.Content);
var combined = guardians.Zip(patients, (g, p) => Tuple.Create(g, p)).ToList();
It would be far more easier to just read the JSON at once, it a single object.
It can't be done with 1 call with the types that you show. You can try using the generic <T> approach for each type, also you'll need to use arrays or lists for the return type because the source JSON is an array:
var guardians = JsonConvert.DeserializeObject<Guardian[]>(response.Content);
var patients = JsonConvert.DeserializeObject<Patient[]>(response.Content);
And then combine the two if you need them to be paired. E.g. if you are sure that you always have just one of each:
var pair = new Pair(guardians[0], patients[0]);
You could make a type to house the two subobjects:
[JsonConverter(typeof(GuardianPatientConverter))]
class GuardianPatient
{
public Guardian Guardian { get; set; }
public Patient Patient { get; set; }
}
And then create a JSON converter to handle the JSON:
class GuardianPatientConverter : JsonConverter
{
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return typeof(GuardianPatient) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
var jObject = JObject.Load(reader);
var guardian = new Guardian();
var patient = new Patient();
serializer.Populate(jObject.CreateReader(), guardian);
serializer.Populate(jObject.CreateReader(), patient);
return new GuardianPatient()
{
Guardian = guardian,
Patient = patient
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then you can use it like so:
var json = "[{\"guardian_id\":\"1453\",\"guardian_name\":\"Foo Bar\",\"patient_id\":\"938\",\"patient_name\":\"Foo Bar\",}]";
var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json);
and if you want it as an array of pairs:
var objects = JsonConvert.DeserializeObject<IEnumerable<GuardianPatient>>(json)
.Select(o => new Pair(o.Guardian, o.Patient))
.ToArray();
This won't make it any faster, but I suspect you're looking for an easier way to work with the JSON.
One another approach would be creating class that matches JSON format, i.e. class with four properties with corresponding names. Then, deserialize JSON into that class and then use it in your code (set properties of objects with values from JSON, pass deserialized object to constructor of another class).
In your models, The name properties need to be strings, instead of integers. After correcting it.
You can use Tuple class
string json = #"[{'guardian_id':'1453','guardian_name':'Foo Bar','patient_id':'938','patient_name':'Foo Bar'}]";
var combination = new Tuple<List<Guardian>, List<Patient>>(JsonConvert.DeserializeObject<List<Guardian>>(json), JsonConvert.DeserializeObject<List<Patient>>(json));
static void Main(string[] args)
{
string json = JsonConvert.SerializeObject(new[]
{
new
{
guardian_id = "1453",
guardian_name = "Foo Bar",
patient_id = "938",
patient_name = "Bar Foo",
}
});
Guardian[] guardians = JsonConvert.DeserializeObject<Guardian[]>(json);
Patient[] patients = JsonConvert.DeserializeObject<Patient[]>(json);
}
Since both your objects are the same, wouldn't it make more sense to just have an ID/Name structure of a single base class? If you need to send all the data at the same time, you can restructure your data and use a data transfer object pattern. The JSON object would become
[{
"guardian": {
"id": "1453",
"name": "Foo Bar"
},
"patient": {
"id" : "938",
"name": "Foo Bar"
}
}]
And your corresponding data objects would be:
public class Record {
public int id { get; set; } // or string. I'm not sure which would be more appropriate
public string name { get; set;}
}
and
public class RecordDto {
public Record guardian { get; set; }
public Record patient { get; set; }
}
And your API would receive a
List<RecordDto>
parameter (since you are passing an array of objects).
When deserializing a JSON property from a request I want to use it for two different properties on my object. e.g;
public class Example
{
[JsonProperty(PropertyName = "favoriteColor")]
public string favoriteColor { get; set; }
[JsonProperty(PropertyName = "favoriteColor")]
public string oldFavoriteColor { get; set; }
}
However this causes an error:
A member with the name 'favoriteColor' already exists on 'Example'. Use the JsonPropertyAttribute to specify another name.
How do I do this when that's exactly what I intend?
I think you could modify the set method for one of the properties so that whenever it is set, it also sets the other property
e.g.
public class Example
{
[JsonProperty(PropertyName = "favoriteColor")]
public string favoriteColor {
get { return favoriteColor; }
set
{
favoriteColor = value;
if (oldFavoriteColor == null) {
oldFavoriteColor = value;
}
}
}
public string? oldFavoriteColor { get; set; }
}
There may be a better answer that involves taking a step back from the problem and seeing whether there's a totally different approach. In particular, why do you need a class that has two properties with the same value? Other than deserializing from JSON, will every other consumer of this class know that these two properties need to be kept in sync, and why?
But this answers the immediate question.
The [JsonProperty] attribute is used for both serializing and deserializing. If you had two properties with the same [JsonProperty] attribute then when you serialized the object to JSON you would have two properties with the same name.
You can create a custom JSON serializer like this. As you can see, after an Example is deserialized it will populate the favoriteColor property with the value of the oldFavoriteColor property.
public class ExampleConverter : CustomCreationConverter<Example>
{
public override Example Create(Type objectType)
{
return new Example();
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
var result = (Example)base.ReadJson(reader, objectType, existingValue,
serializer);
result.favoriteColor = result.oldFavoriteColor;
return result;
}
}
In order for this to work you also have to tell the serializer not to attempt to deserialize the favoriteColor property. Even without the [JsonProperty] attribute there's still a conflict between [JsonProperty(PropertyName = "favoriteColor")] and another property actually named "favoriteColor."
public class Example
{
[JsonIgnore]
public string favoriteColor { get; set; }
[JsonProperty(PropertyName = "favoriteColor")]
public string oldFavoriteColor { get; set; }
}
A unit test to confirm:
public void DeserializerPopulatesFavoriteColorFromOldFavoriteColor()
{
var json = #"{ favoriteColor: ""Green""}";
var deserialized = JsonConvert.DeserializeObject<Example>(json, new ExampleConverter());
Assert.AreEqual("Green", deserialized.oldFavoriteColor);
Assert.AreEqual(deserialized.oldFavoriteColor, deserialized.favoriteColor);
}
I am trying to parse a JSON feed using LINQ and can't quite wrap a list of objects/values into a reasonable class.
My JSON looks like:
{
"Project":[
{
"ID":"XY1212",
"Name":"Some Name",
"Description":"U.S. No 2 Diesel Retail Prices",
"Manager":"Nora Sims",
"Dept":"HR",
"Updated":"2014-07-22",
"Statistics":[
[
"20140722",
32.22
],
[
"20140721",
55
],
[
"20140720",
343
],
[
"20140519",
43
],
[
"20140421",
3.971
],
[
"20140211",
40.2
],
[
"20140210",
17
],
[
"20140209",
16
]
]
}
]
}
From the above JSON, I have the following class structure:
public class Project
{
public string ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Manager { get; set; }
public string Dept { get; set; }
public string Updated { get; set; }
public List<List<object>> Statistics { get; set; }
}
public class RootObject
{
public List<Project> Project { get; set; }
}
public class Statistic
{
public Datetime ProjectDate { get; set; }
public Decimal Sale { get; set; }
}
I am trying to parse the feed and just want the "Statistics" data, but not sure how to get all of the values into the collection of "Statistics":
HttpClient() http = new HttpClient();
var json = await http.GetStringAsync(uri);
JObject jo = JObject.Parse(json);
var jList = from values in jo["Project"].Children()["Statistics"]
select values;
When I inspect jList via the following loop:
foreach (var stat in jList)
{
Console.WriteLine(stat);
}
I can "see" all of the values, but it only loops once, i.e. jList is only one big [0] with a "value" of all of the [x, y], [x1, y1], ..., i.e. looks like an array of one dimension with many 2D arrays inside it.
I want to loop through all of the "arrays", I believe that's what they are, within the [0] I see in Visual Studio while debugging.
Any advice appreciated.
You can solve this easily by making a custom JsonConverter for your Statistic class. It will deal with the unconventional JSON and allow you to define your Project class the way you'd really like to have it.
Here is the code for the converter:
class StatisticConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Statistic));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
return new Statistic
{
ProjectDate = DateTime.ParseExact(array[0].ToString(), "yyyyMMdd",
System.Globalization.CultureInfo.InvariantCulture),
Sale = array[1].ToObject<decimal>()
};
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, we just need to make a couple of minor changes to your classes. First, change the Statistics property to be a List<Statistic> instead of a List<List<object>>. (Don't worry that it doesn't match the JSON-- that's what the converter is for.)
public class Project
{
public string ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Manager { get; set; }
public string Dept { get; set; }
public string Updated { get; set; }
public List<Statistic> Statistics { get; set; }
}
Next decorate your Statistic class with a [JsonConverter] attribute to tie it to the custom converter:
[JsonConverter(typeof(StatisticConverter))]
public class Statistic
{
public DateTime ProjectDate { get; set; }
public Decimal Sale { get; set; }
}
That's it! Now you can deserialize as normal, and you'll get a list of statistics the way you want.
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
Working demo here.
The problem isn't your code so much as it is the JSON string that is coming back from the HttpClient.
Because it has parts that don't have a key, it is hard to sort through, also each child of statistic is a child of that child (If my eyes don't deceive me).
However, using the code below, I am able to read the Statistics values singularly
JObject jo = JObject.Parse(json);
var jList = from values in jo["Project"]
select values;
foreach (var j in jList)
{
var l = j["Statistics"].Children();
foreach (var m in l.Children())
{
string a = m.ToString();
}
}
On the first loop, m has the Date, On the second loop it has the sale. because there is no key for these, I don't know any other way to reference them.
A bit less code but similar result
JObject jo = JObject.Parse(json);
for (int i = 0; i < jo.Count; i++)
{
var jList = from values in jo["Project"][i]["Statistics"]
select values;
foreach (var stat in jList)
{
//here stat looks like {["20140722", 32.22]}
}
}