Assign the same JsonProperty to two properties on class - c#

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);
}

Related

Deserialize JSON with dynamic keys using C# Json.NET

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

Deserializing JSON response which differ in each response

I have complicated JSON respone which containt about 100 attributes/objects/arrays and have difrrent responses in terms of object/array.
Firstly I have structure like that (when object exists)
{
'att1': 'desc',
'att2': '83482',
'att3': null,
'test': {
'object_array1': [
100
],
'object_array2': [
'desc'
]
}
}
public class Root
{
//fields here
public Test test { get; set; }
}
public class Test
{
public List<int> object_array1 { get; set; }
public List<string> object_array2 { get; set; }
}
The issue I have is when this objects is empty.
After that resposne is changing and returning empty array.
So it is chaning to this:
{
'att1': 'desc',
'att2': '83482',
'att3': null,
'test': [
]
}
And beacause of that I have standard error:
Additional information: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Test' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
I was trying to write customconverter with something like that:
private bool IsArray(string fieldName, JObject jObject)
{
return jObject[fieldName].Type == JTokenType.Array;
}
I'm using JSON.NET
But I failed miserably. Any help would be much appreciated
Use custom converter which would check token type that has been read in the ReadJson method and substitute some default value for test when the token is of type JArray (assuming it can only be an array when the test object is "empty"):
public class Root
{
[JsonConverter(typeof(TestIgnoreEmptyListConverter))]
public Test test { get; set; }
}
// .................
public class TestIgnoreEmptyListConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Test).IsAssignableFrom(objectType) || objectType.IsArray;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
if (token is JArray)
return default(Test);
else
return token.ToObject<Test>();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Demo: https://dotnetfiddle.net/Q3S5hX
Your JSON propert name does not match object's name. Serializer tries to serialize/deserialize using property name or attributes.
Change this to the right name
public Test test { get; set; }
Or use attribute to set json name for a property.
[JsonProperty("test")]
public Test objects { get; set; }
Or even better, refactor your code to respect C# conventions. (I suggest to rename fields to be more descriptive)
public class Root
{
[JsonProperty("test")]
public Test Test { get; set; }
}
public class Test
{
[JsonProperty("object_array1")]
public List<int> IntArray { get; set; }
[JsonProperty("object_array2")]
public List<string> StringArray { get; set; }
}

Deserialise IList<Interface> in JSON.NET

I have some json that I want to Deserialise. It's very easy to deserialise back to an List However I am working with interfaces, and this is what I need the deserialisation to end up at:
So Let's start with some JSON:
[{"TimeGenerated":"2017-05-30T19:21:06.5331472+10:00","OuterValue":1.08,"Identifier":{"IdentifierName":"BLA","IdentifierNumber":1},"Locale":{"LocaleName":"LOCALE NAME","LocaleType":1,"LocaleNumber":1,"StartTime":"2017-05-30T19:20:00+10:00"},"InnerValue":1.08,"InnerType":0,"InnerId":"InnerI","Type":"MyType","Id":"11111"}]
This is the data that is Serialised on the server side (without issue of course) and it's object type is :
IList<IRootObject>
So effectively, I want to deserialise the other side as:
IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString);
Obviously the pickle here is the old interface/abstract type error of:
'Could not create an instance of type BLA. Type is an interface or abstract class and cannot be instantiated'
Fine, I create some custom converters and decorate as follows:
Classes are:
[JsonObject(MemberSerialization.OptIn)]
public class Identifier
{
[JsonProperty]
public string IdentifierName { get; set; }
[JsonProperty]
public int IdentifierNumber { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class Locale
{
[JsonProperty]
public string LocaleName { get; set; }
[JsonProperty]
public int LocaleType { get; set; }
[JsonProperty]
public int LocaleNumber { get; set; }
[JsonProperty]
public string StartTime { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class RootObject:IRootObject
{
[JsonProperty]
public string TimeGenerated { get; set; }
[JsonProperty]
public double OuterValue { get; set; }
[JsonProperty]
[JsonConverter(typeof(ConcreteConverter<Identifier>))]
public IIdentifier Identifier { get; set; }
[JsonProperty]
[JsonConverter(typeof(ConcreteConverter<Locale>))]
public ILocale Locale { get; set; }
[JsonProperty]
public double InnerValue { get; set; }
[JsonProperty]
public int InnerType { get; set; }
[JsonProperty]
public string InnerId { get; set; }
[JsonProperty]
public string Type { get; set; }
[JsonProperty]
public string Id { get; set; }
}
Interface is:
public interface IRootObject
{
string TimeGenerated { get; set; }
double OuterValue { get; set; }
Identifier Identifier { get; set; }
Locale Locale { get; set; }
double InnerValue { get; set; }
int InnerType { get; set; }
string InnerId { get; set; }
string Type { get; set; }
string Id { get; set; }
}
Concrete Converter is as follows:
public class ConcreteConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
try
{
var s = serializer.Deserialize<T>(reader);
return s;
}
catch (Exception ex)
{
return null;
}
// return serializer.Deserialize<T>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
I also have a list converter which I have made (seeing this is a list albeit this is unused, but here JIC it is needed):
public class ConcreteListConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (existingValue == null)
return null;
var res = serializer.Deserialize<List<TImplementation>>(reader);
return res.ConvertAll(x => (TInterface)x);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
So the issues are:
When I run:
IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString);
I always get a null returned.
When i put breakpoints on my ConcreteConverter CanConvert/ReadJson/WriteJson methods - they are never hit.
What am I missing, and how do i get the magic of:
IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString);
to work properly.
I must note, I DO NOT want to just typnamehandling.objects (i think this is it) where it adds the ASSEMBLYNAME.TYPE etc into the json, thus bloating it. Just assume that the json above is what we have to work with, and the classes represented above, with their interfaces is what it must be deserialised to.
Regards,
Chud
PS - I have changed the class names/properties etc away from the actual real world implementation of what I am doing. So if they have come out with an error, I do apologise. But hopefully the majority is all ok, and the point/ultimate goal is understood :)
The Problem
You cannot create instances of an interface, that is not allowed in .NET. You cannot do this:
var items = new IList<int>(); // Not allowed
Your error is clearly stating that:
Could not create an instance of type BLA. Type is an interface or abstract class and cannot be instantiated'
The Solution
Deserialize it to type List but assign it to type IList. Please note the types on the right side are not interfaces in code below except at the casting stage.
IList<IRootObject> MyDeserialisedObject =
JsonConvert.DeserializeObject<List<RootObject>>(File.ReadAllText(JsonString))
.Cast<IRootObject>().ToList();
Some More Belabor
You may ask "Why do I need to cast" and why can I not do this instead?
IList<IRootObject> MyDeserialisedObject =
JsonConvert.DeserializeObject<List<RootObject>>(File.ReadAllText(JsonString));
That is not allowed in order to preserve reference identity.
You are trying to get JsonConvert to instantiate interfaces, which is not possible.
The problem is here:
IList<IRootObject> MyDeserializedObject = JsonConvert.Deserialize<IList<IRootObject>>(JsonString);
JsonConvert will process the json data you provide it.
Because you ask to convert it to an object of type IList<IRootObject>, JsonConvert will iteratively build a list of IRootObject objects.
For every element found in the list (inside the json data string), JsonConvert will create a new IRootObject() and populate its properties with the data it finds in the json data.
And that's the problem: new IRootObject() is not possible, because you cannot instantiate an interface. You can only instantiate classes.
Solution:
You already have a class RootObject. It already contains the same fields that your interface contains. However, RootObject is currently not implementing IRootObject.
Add the interface implementation to the class:
public class RootObject : IRootObject
{
//make sure the implementation is correct
}
And then you can ask JsonConvert to give you a list of RootObject (not IRootObject!)
List<RootObject> MyDeserializedRootObjects = JsonConvert.Deserialize<List<RootObject>>(JsonString);
And if you want to turn the List<RootObject> into a IList<IRootObject>:
IList<IRootObject> MyDeserializedIRootObjects = MyDeserializedRootObjects.ToList<IRootObject>();
In case you are using .NET 3.5 or lower, you can cast the list as follows:
IList<IRootObject> MyDeserializedIRootObjects = MyDeserializedRootObjects .Cast<IRootObject>().ToList();
edit I may have done it a bit sneakily, but for the same reason that you cannot instantiate an IRootObject, you also cannot instantiate an IList. Both of these are interfaces.
The solution is the same for both: instantiate an object of a class that implements the interface (List and RootObject), and then cast them to their interfaces (IList and IRootObject).
I believe you are never using your custom converter. According to the Newtonsoft.Json documentation (here), you should be using something like this:
IList<IRootObject> MyDeserialisedObject = JsonConvert.Deserialise<IList<IRootObject>>(JsonString, new ConcreteListConverter());
Ignore the i++ that's just my iteration through my database columns. My reader here was reading a JSON object of type Student from the database and I used an IList like so and it worked. If it provides more clarity, IList is an interface and List is a concrete class or better yet List is a concrete type that implements the IList interface.
course.Student = reader.DeserializeObject<IList<Student>>(i++);

How to prevent a single object property from being converted to a DateTime when it is a string

Here is a simplified version of the model I have to work with:
class InputModel
{
public string Name { get; set; }
public object Value { get; set; }
}
And the relevant parts of the controller:
class Controller : ApiController
{
[HttpPut]
public async Task<IHttpActionResult> Update([FromBody]InputModel model)
{
//implementation
}
}
the Value property of the InputModel class could be of any type, and which type it will be is only known later on, in a piece of legacy code the model is sent to and that i have no control over.
The problem I have occurs with the following json in the request body:
{
"name": "X",
"value": "2001-10-17T13:55:11.123"
}
The default behavior is to parse this json so that the Value property gets converted to a DateTime. DateTimes however are handled very differently from strings in the legacy code and data is lost after handling it (for example: the millisecond part is removed when persisting to the database). So when the same value is later requested, the returned value is "2001-10-17T13:55:11" (milliseconds missing).
Of course I can fix this by globally setting this in my web api configuration:
httpConfiguration.Formatters.JsonFormatter.SerializationSettings.DateParseHandling = DateParseHandling.None;
But doing so disables parsing DateTimes also for models in other methods and controllers that have models for which the default behavior is wanted.
What I'm looking for is something like the following (imaginary) code:
class InputModel
{
public string Name { get; set; }
[JsonSerializerSettings(DateParseHandling = DateParseHandling.None)]
public object Value { get; set; }
}
But I can't find out how to achieve this. Any help would be greatly appreciated.
What one can do is to add a custom JsonConverter to the InputModel type to temporarily toggle JsonReader.DateParseHandling to None:
[JsonConverter(typeof(DateParseHandlingConverter), DateParseHandling.None)]
class InputModel
{
public string Name { get; set; }
public object Value { get; set; }
}
public class DateParseHandlingConverter : JsonConverter
{
readonly DateParseHandling dateParseHandling;
public DateParseHandlingConverter(DateParseHandling dateParseHandling)
{
this.dateParseHandling = dateParseHandling;
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var old = reader.DateParseHandling;
try
{
reader.DateParseHandling = dateParseHandling;
existingValue = existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
serializer.Populate(reader, existingValue);
return existingValue;
}
finally
{
reader.DateParseHandling = old;
}
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Note that, if the JSON to be deserialized contains nested arrays or objects, all recursively contained values will be parsed with DateParseHandling.None.
One might ask, why not add a converter directly to the property, like so?
class InputModel
{
public string Name { get; set; }
[JsonConverter(typeof(DateParseHandlingConverter), DateParseHandling.None)]
public object Value { get; set; }
}
It turns out that this does not work because, at the time JsonConverter.ReadJson() is called, the reader has already advanced to the date string and tokenized it as a DateTime. Thus the converter must be applied to the containing type.

Why is Json Dot Net not even trying to evaluate my interface properties?

I have an object where I'm trying to intercept the deserialization process for two properties which are interfaces. I'm using Json Dot Net.
The issue that I'm facing is that Json Dot Net doesn't even try to convert the two IDataStore properties. In other words, 'CanConvert' is never even run for those types.
I've tried adding JsonProperty and JsonConverter attributes and still no dice.
Any insight would be appreciated.
Edit (and Answer) After being asked by Ron Beyer to post the JSON, it became clear that Json Dot Net doesn't even consider operating on a property if the source JSON doesn't have it. When adding '"SourceDataStore":{}' it tries to convert that property. I was thinking it would look at the current object type and iterate over that but there's obviously a matching process beforehand
JSON
{
"Name":"My First Definition",
"SourceDataStoreType":"SqlDataStore",
"DestinationDataStoreType":"MongoDataStore"
}
Class
internal class Definition
{
public string Name { get; set; }
public DataStoreTypes SourceDataStoreType { get; set; }
public DataStoreTypes DestinationDataStoreType { get; set; }
public IDataStore SourceDataStore { get; set; }
public IDataStore DestinationDataStore { get; set; }
public Definition()
{
}
}
Converter
public class DataStoreConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
var b = objectType == typeof(IDataStore);
return b;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return new SqlDataStore();
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Code
var definition = JsonConvert.DeserializeObject<Definition>(definitionsJson, new JsonSerializerSettings
{
Converters = new[] { new DataStoreConverter() }
});
return definition;
(Comments to an answer)
I asked for the JSON example to verify that 1) the JSON was well formatted and 2) included the properties in question.
The issue, it turns out that you figured out, is that the JSON deserializer will not attempt to run the conversion on any properties it does not find in the JSON. This is because the deserializer parses the JSON and looks for appropriate properties in the object to fill in. If the property doesn't exist in the JSON, it won't attempt to build that property from nothing.
There are two solutions to this, either you can add a null (blank) property for those in the JSON, or you can edit your object to create them when you run the constructor, something like:
internal class Definition
{
public string Name { get; set; }
public DataStoreTypes SourceDataStoreType { get; set; }
public DataStoreTypes DestinationDataStoreType { get; set; }
public IDataStore SourceDataStore { get; set; }
public IDataStore DestinationDataStore { get; set; }
public Definition()
{
var container = YourIOCContainerHere.Instance;
SourceDataStore = container.Resolve<IDataStore>();
DestinationDataStore = container.Resolve<IDataStore>();
//Or, without using IOC/DI
SourceDataStore = new SqlDataStore();
DestinationDataStore = new SqlDataStore();
}
}

Categories