How to add JsonPropertyName to inherited properties? - c#

How do I deserialize json to my class that is inheriting from base class but properties in base class do not match json? I cant's control base class. For example:
{
"prop1": "Value1",
"prop2": "Value2"
}
I want to deserialize above to MyClass:
public class MyClass : Base
{
[JsonPropertyName("prop2")]
public string? Property2 { get; set; }
}
public class Base // I do not own this
{
public string? Property1 { get; init; }
}
Should I override Base properties and decorate them?

If you want to use same property name then you should hide base class property with new keyword and mark it with JsonPropertyName attribute:
public class MyClass : Base
{
[JsonPropertyName("prop2")]
public string? MyProperty2 { get; set; }
[JsonPropertyName("prop1")]
public new string? Property1 { get; set; }
}
Also you can implement JsonConverter for type instead to find a specific properties in JSON and map them to object properties. This will keep your model clean from a specific of JSON model.
public class MyClassJsonConverter : JsonConverter<MyClass>
{
public override MyClass Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var jsonDocument = JsonDocument.ParseValue(ref reader);
var rootElement = jsonDocument.RootElement;
var myClass = new MyClass();
myClass.Property1 = rootElement.GetProperty("prop1").GetString();
myClass.Property2 = rootElement.GetProperty("prop2").GetString();
return myClass;
}
public override void Write(Utf8JsonWriter writer, MyClass value, JsonSerializerOptions options)
{
// imlement logic here if needed
throw new NotImplementedException();
}
}
[JsonConverter(typeof(MyClassJsonConverter))]
public class MyClass : Base
{
public string? MyProperty2 { get; set; }
}
Also here is a big detailed article "How to write custom converters for JSON serialization (marshalling) in .NET" with examples for converters, converter factory, error handling converters registration and other aspects about converters for JSON serialization.
For example you don't have to use JsonConverterAttribute and call Deserialize with explicitly specified converters:
public class MyClass : Base
{
public string? MyProperty2 { get; set; }
}
var serializeOptions = new JsonSerializerOptions
{
WriteIndented = true,
Converters =
{
new MyClassJsonConverter()
}
};
var myClass = JsonSerializer.Deserialize<MyClass>(jsonString, deserializeOptions)!;

you can use a custom JsonConverter from #VadimMartynov, but more simple way is to add a property
public class MyClass : Base
{
[JsonPropertyName("prop2")]
public string? Property2 { get; set; }
[JsonPropertyName("prop1")]
public new string? Property1
{
get { return base.Property1; }
init { base.Property1 = value; }
}
}
but I recommend you to start to use Newtonsoft.Json. This code would be much more clear
public class MyClass : Base
{
[JsonProperty("prop2")]
public string? Property2 { get; set; }
[JsonConstructor]
public MyClass(string? prop1)
{
Property1 = prop1;
}
}

Related

Convert JSON to list with generic types

I got a JSON response like:
{
"items": [
{
"document": {
"id": "123",
"title": "title2",
"description": "Description1",
"type": "Typ1"
}
},
{
"document": {
"id": "456",
"title": "title2",
"description": "desctiption2",
"type": "Type2",
"Type2Property": "Type2Property"
}
}
]
}
As you can see above I have two values (just for example) with different properties.
In my code, I have two classes.
public class Type1
{
public string Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Type { get; set; }
}
public class Type2
{
public string Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Type { get; set; }
public string Type2Property {get; set;}
}
Question: How can I create one generic list which combines Type1 and Type2. In the future, I can have more TypeX (with different properties). So, I'd like to parse JSON into a generic list.
Update: I can filter json by the Type property from the JSON.
One way to solve this problem is to create a custom JsonConverter and override its ReadJson method.
I've introduced a couple of helper classes to be able to parse the whole sample json:
public class TopLevel
{
public MidLevel[] Items { get; set; }
}
public class MidLevel
{
public IDocument Document { get; set; }
}
[JsonConverter(typeof(DocumentTypeConverter))]
public interface IDocument
{
}
I've created an IDocument marker interface. If you wish you can use abstract class.
I've decorated the interface with a JsonConverterAttribute and specified there the custom converter.
I've changed the Type1 and Type2 classes to implement this interface:
public class Type1 : IDocument
{
...
public string Type { get; set; }
}
public class Type2 : IDocument
{
...
public string Type { get; set; }
public string Type2Property { get; set; }
}
The DocumentTypeConverter naive implementation would look like this:
(Obviously you can make more type-safe)
public class DocumentTypeConverter : 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)
{
var jObject = JObject.Load(reader);
switch (jObject["type"].Value<string>())
{
case "Typ1":
{
var obj = new Type1();
serializer.Populate(jObject.CreateReader(), obj);
return obj;
}
case "Type2":
{
var obj = new Type2();
serializer.Populate(jObject.CreateReader(), obj);
return obj;
}
default:
throw new Exception();
}
}
public override bool CanConvert(Type objectType)
=> objectType == typeof(IDocument);
}
The CanConvert tells us that this convert can be used against IDocuments.
The ReadJson branches its logic based on the "type" field.
The actual conversion done with the Populate instead of JsonCovert.Deserialize to avoid infinite recursion.
Finally, the usage is that simple:
static void Main(string[] args)
{
var sampleJson = File.ReadAllText("sample.json");
var sample = JsonConvert.DeserializeObject<TopLevel>(sampleJson);
Console.ReadLine();
}

Copy instances of derived class based on properties with attributes

Suppose I have an abstract base class in which I want a CreateCopy method:
public abstract class BaseClass
{
///base stuff
public BaseClass CreateCopy() //or public object, if necessary
{
//?????
}
}
Assume that all derived classes have a parameterless constructor and property fields (that can be) marked with some kind of attribute:
public class DerivedClass : BaseClass
{
[CopiableProperty]
public string Property1 {get; private set;}
[CopiableProperty]
public int Property2 {get; private set;}
//no need to copy
public int Property3 {get; private set;}
//parameterless constructor
public DerivedClass() { }
}
Is it possible with this structure to write the body of CreateCopy() in a way that I can create new instances of the derived objects with the correct CopiableProperty fields?
Naturally I could make a public abstract BaseClass CreateCopy() and force each derived class to care for its own copy, but due to the size and amount of the derived classes, this would bring too much extra effort.
A fairly simple approach could be using generics and reflection:
public abstract class BaseClass
{
// restrict to children of BaseClass
public T CreateCopy<T>() where T: BaseClass, new()
{
var copy = new T();
// get properties that you actually care about
var properties = typeof(T).GetProperties()
.Where(x => x.GetCustomAttribute<CopiablePropertyAttribute>() != null);
foreach (var property in properties)
{
// set the value to the copy from the instance that called this method
property.SetValue(copy, property.GetValue(this));
}
return copy;
}
}
public class DerivedClass : BaseClass
{
[CopiableProperty]
public string Property1 { get; set; }
[CopiableProperty]
public int Property2 { get; set; }
public int Property3 { get; set; }
public override string ToString()
{
return $"{Property1} - {Property2} - {Property3}";
}
}
static void Main(string[] args)
{
var original = new DerivedClass
{
Property1 = "Hello",
Property2 = 123,
Property3 = 500
};
var copy = original.CreateCopy<DerivedClass>();
Console.WriteLine(original);
Console.WriteLine(copy);
Console.ReadLine();
}
This would print:
Hello - 123 - 500
Hello - 123 - 0
Another approach would be to take advantage of a serialization library, if you don't mind the dependency:
public abstract class BaseClass
{
public BaseClass CreateCopy()
{
string serialized = JsonConvert.SerializeObject(this);
var actualType = GetType();
return JsonConvert.DeserializeObject(serialized, actualType) as BaseClass;
}
}
public class DerivedClass : BaseClass
{
public string Property1 { get; set; }
public int Property2 { get; set; }
[JsonIgnore]
public int Property3 { get; set; }
//parameterless constructor
public DerivedClass() { }
public override string ToString()
{
return $"{Property1} - {Property2} - {Property3}";
}
}
My solution uses serialization/deserialization, using JSON.NET nuget package.
No need for a method in the base class, you can use an extension method instead (adapted from this answer):
using Newtonsoft.Json;
public static class ObjectExtensions
{
public static T Clone<T>(this T source)
{
var serialized = JsonConvert.SerializeObject(source);
var clone = JsonConvert.DeserializeObject<T>(serialized);
return clone;
}
}
And then use attributes to control which properties should be copied or not - example:
using Newtonsoft.Json;
public class DerivedClass : BaseClass
{
public string Property1 { get; set; }
public int Property2 { get; set; }
[JsonIgnore]
public int Property3 { get; set; }
}
Using the code:
var obj1 = new DerivedClass
{
Property1 = "Abc",
Property2 = 999,
Property3 = 123
};
DerivedClass clone = obj1.Clone();
Result - as you can see, Property3 has the default value in the cloned object:
Iterate through all properties in type and check your attribute with GetCustomAttributes. See code:
public BaseClass CreateCopy()
{
var type = GetType();
var result = Activator.CreateInstance(type);
foreach (var propertyInfo in type.GetProperties())
{
var skipThisProperty = !propertyInfo.GetCustomAttributes(
typeof(CopiablePropertyAttribute), false)
.Any();
if (skipThisProperty)
continue;
var value = propertyInfo.GetValue(this, null);
propertyInfo.SetValue(result, value, null);
}
return (BaseClass) result;
}
Please pay attention to null parameter in GetValue and SetValue. If your property is indexer, you need to pass a correct value as last argument

How to deserialize JSON to C# class which uses composition

I need to deserialize
{'Id': 'id123',
'Time': 1436231503,
'Name': 'foo',
'ProductId': 1}
into Container1
public class Container1
{
public CommonFields Common { get; set; }
//fields specific to Container1
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "ProductId")]
public int ProductId { get; set; }
}
and
{
'Id': 'id123',
'Time': 1436231503,
'Group':'10768C21-9971-4D2F-ACD7-10C2EF19FCA8'
}
into Container2
public class Container2
{
public CommonFields Common { get; set; }
//fields specific to Container2
[JsonProperty(PropertyName = "Group")]
public Guid Group { get; set; }
}
using composition (not inheritance). Both JSON have 2 common fields (Id and Time) and specific fields.
With newtonsoft.json
JsonConvert.DeserializeObject<Container1>(json_container1)
the result is that the properties of the 2 container are correctly deserialized. The common properties of the composed classed are not deserialized.
How can I deserialize JSON into C# classes that use only composition?
(using newtonsoft.json is not compulsory)
Below is my attempt.
public class CommonFields
{
[JsonProperty(PropertyName = "Id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "Time")]
public long Time { get; set; }
}
public class Container1
{
public CommonFields Common { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "ProductId")]
public int ProductId { get; set; }
}
public class Container2
{
public CommonFields Common { get; set; }
[JsonProperty(PropertyName = "Group")]
public Guid Group { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
string json_container1 = #"{
'Id': 'id123',
'Time': 1436231503,
'Name': 'foo',
'ProductId': 1
}";
string json_container2 = #"{
'Id': 'id123',
'Time': 1436231503,
'Group':'10768C21-9971-4D2F-ACD7-10C2EF19FCA8'
}";
var container1Obj = JsonConvert.DeserializeObject<Container1>(json_container1);
var container2Obj = JsonConvert.DeserializeObject<Container2>(json_container2);
Console.ReadKey();
}}}
Don't do it.
The JSON element you deserilize from should not be change, you can remove some properties but it's a bad practice to change its properties structure.
JSON file\content should have a compatible JSON class, if you want to make any changes, make another custom class and make a mapping logic between them.
I think you can just do the deserialize again just on CommonFields of two objects.
container1Obj.Common = JsonConvert.DeserializeObject<CommonFields>(json_container1);
container2Obj.Common = JsonConvert.DeserializeObject<CommonFields>(json_container2);
I know you want to use composition, but I really can't see any pros using composition over inheritance here.
public class BaseClass
{
[JsonProperty(PropertyName = "Id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "Time")]
public long Time { get; set; }
}
public class Container1 : BaseClass
{
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "ProductId")]
public int ProductId { get; set; }
}
public class Container2 : BaseClass
{
[JsonProperty(PropertyName = "Group")]
public Guid Group { get; set; }
}
This is as simple as it can get and should get the job done.
Your question basically the reverse of the question Can I serialize nested properties to my class in one operation with Json.net?, and can be solved with a similar strategy. To simplify things, create an interface for all classes containing common fields:
public interface IHasCommonFields
{
CommonFields Common { get; set; }
}
Then you can create the following generic converter for any type implementing this interface:
public class HasCommonFieldsConverter<T> : JsonConverter where T : IHasCommonFields
{
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanWrite { get { return !Disabled; } }
public override bool CanRead { get { return !Disabled; } }
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
if (token == null || token.Type == JTokenType.Null)
return null;
using (new PushValue<bool>(true, () => Disabled, val => Disabled = val)) // Prevent infinite recursion of converters
{
var hasCommon = token.ToObject<T>(serializer);
var common = (hasCommon.Common ?? (hasCommon.Common = new CommonFields()));
serializer.Populate(token.CreateReader(), common);
return hasCommon;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
{
writer.WriteNull();
return;
}
using (new PushValue<bool>(true, () => Disabled, val => Disabled = val)) // Prevent infinite recursion of converters
{
var hasCommon = (T)value;
var obj = JObject.FromObject(hasCommon, serializer);
var common = hasCommon.Common;
if (common != null)
{
var commonObj = JObject.FromObject(common, serializer);
obj.Merge(commonObj);
}
obj.WriteTo(writer);
}
}
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
Then apply it to all classes implementing IHasCommonFields - and in addition mark the Common property with JsonIgnore:
[JsonConverter(typeof(HasCommonFieldsConverter<Container1>))]
public class Container1 : IHasCommonFields
{
[JsonIgnore]
public CommonFields Common { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "ProductId")]
public int ProductId { get; set; }
}
[JsonConverter(typeof(HasCommonFieldsConverter<Container2>))]
public class Container2 : IHasCommonFields
{
[JsonIgnore]
public CommonFields Common { get; set; }
[JsonProperty(PropertyName = "Group")]
public Guid Group { get; set; }
}
Then, to test:
public class TestClass
{
public static void Test()
{
var container1 = new Container1 { Name = "name", ProductId = 101, Common = new CommonFields { Id = "1401", Time = DateTime.Today.Ticks } };
var container2 = new Container2 { Group = Guid.NewGuid(), Common = new CommonFields { Id = "2401", Time = DateTime.Today.Ticks } };
Test(container1);
Test(container2);
}
private static void Test<T>(T container) where T : class, IHasCommonFields
{
var json = JsonConvert.SerializeObject(container, Formatting.Indented);
Debug.WriteLine(json);
var containerback = JsonConvert.DeserializeObject<T>(json);
var json2 = JsonConvert.SerializeObject(containerback, Formatting.Indented);
Debug.Assert(json == json2); // No assert
if (container.Common != null)
{
Debug.Assert(container.Common.Id == containerback.Common.Id); // No assert
Debug.Assert(container.Common.Time == containerback.Common.Time); // No assert
}
}
}
The JSON created looks like:
{
"Name": "name",
"ProductId": 101,
"Id": "1401",
"Time": 635725152000000000
}
And
{
"Group": "9ed31118-c0b7-4d9f-8f57-303b2e164643",
"Id": "2401",
"Time": 635725152000000000
}
The one disadvantage of this converter is that, if the Common property is null when serialized, it will be deserialized non-null with default values.
If you don't want the IHasCommonFields interface, you could e.g. create an abstract generic base class for your converter with abstract methods to get and set the common fields, then override those methods in each subclass.
(Honestly inheritance does seem simpler than composition here, as other answers have stated.)

XmlSerializer - Create secondary attributes to base types eg string

Using .net XmlSerializer and the following structure:
public class SomeClass {
[XmlElement("some-string")]
public string SomeString { get; set; }
}
I need the above to produce :
<someclass>
<some-string alt-name="someotherstring">
StringValue
</some-string>
</someclass>
But i dont want to have to define types for somestring, some int, somebool, yetanotherstring etc every time i want to add a standard type as a porperty to my classes.
Any way I can override xlement to handle this maybe?
Produce wrappers for base types and conversion operators to alleviate object construction:
[Serializable()]
public partial class StringWrapper
{
[XmlAttribute("alt-name")]
public string altname { get; set; }
[XmlText()]
public string Value { get; set; }
public static implicit operator string (StringWrapper sw) { return sw.Value; }
public static implicit operator StringWrapper (string s) {
return new StringWrapper() { altname = "someotherstring", Value = s };
}
}
Use wrappers instead of base types where needed:
[Serializable()]
[XmlRoot(Namespace = "someclass", IsNullable = false)]
public class someclass
{
[XmlElement("some-string")]
public StringWrapper somestring { get; set; }
}
Use it like:
var srlz = new XmlSerializer(typeof(someclass));
srlz.Serialize(Console.Out, new someclass() { somestring = "StringValue" });
The only way to do that via XmlSerializer is:
[XmlRoot("someclass")]
public class SomeClass {
[XmlElement("some-string")]
public SomeOtherClass Foo {get;set;}
}
public class SomeOtherClass {
[XmlText]
public string Text {get;set;}
[XmlAttribute("alt-name")]
public string Bar {get;set;}
}
Alternatively: use XmlDocument / XDocument instead of XmlSerializer.

How to parse json to enum from different sources

I'm getting data in json format from different sources and I'm trying to map them to objects implementing the same interface.
The json variable looks something like this from feed 1:
{"identifier": 232, "type": "Feed1"}
And I'm serializing it using this object:
[DataContract]
public class Class A : InterfaceA
{
[DataMember(Name = "identifier")]
public int Id{ get; set; }
[DataMember(Name = "type")]
public FeedType Type { get; set; }
}
[DataContract]
public enum FeedType
{
[EnumMember(Value = "Feed1")]
FeedA,
[EnumMember(Value = "Feed2")]
FeedB,
[EnumMember(Value = "Feed3")]
FeedC
}
The interface looks like this:
public interface InterfaceA
{
int Id {get;set;}
FeedType Type{get;set;}
}
In feed 2, the object looks like this:
{"identifier": 232, "feedType": "A"}
How can I create another object that implements the same interface and will return the same enum? How do I set up the DataContract?
EDIT:
I serialize it like this
var serializer = new DataContractJsonSerializer(ClassA);
var ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
var serializedObject = serializer.ReadObject(ms);
I'll give an answer using Json.Net (of course if you are open to use a different serializer)
string json = #"{""identifier"": 232, ""type"": ""Feed2""}";
var classa = JsonConvert.DeserializeObject<ClassA>(json);
.
public interface InterfaceA
{
int Id { get; set; }
FeedType Type { get; set; }
}
public class ClassA : InterfaceA
{
[JsonProperty("identifier")]
public int Id{ get; set; }
[JsonConverter(typeof(MyConverter))] //<--- !!!
[JsonProperty("type")]
public FeedType Type { get; set; }
}
[DataContract]
public enum FeedType
{
[EnumMember(Value = "Feed1")]
FeedA,
[EnumMember(Value = "Feed2")]
FeedB,
[EnumMember(Value = "Feed3")]
FeedC
}
And this is the type converter class
public class MyConverter : Newtonsoft.Json.Converters.StringEnumConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(FeedType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var field = objectType.GetFields()
.First(f => f.GetCustomAttributes(false)
.Any(a=>a.GetType()==typeof(EnumMemberAttribute) &&
((EnumMemberAttribute)a).Value.Equals(reader.Value)));
return field.GetValue(null);
}
}
so if you want a class B
[DataContract]
public class ClassB : InterfaceA
{
[DataMember(Name = "identifier")]
public int Id{ get; set; }
[DataMember(Name = "type")]
public FeedType Type { get; set; }
}
var serializer = new DataContractJsonSerializer(ClassB);
var ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
var serializedObject = serializer.ReadObject(ms);
Thats it?! you could use a generic if you wanted to reuse the code:
public T SerialiseObject<T>(string json) where T : InterfaceA
{
var serializer = new DataContractJsonSerializer(T);
var ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
return (T)serializer.ReadObject(ms);
}
then call it:
SerialiseObject<ClassA>(json);
SerialiseObject<ClassB>(json);
to explain more fully you could not have
public class ClassA : InterfaceA
{
public ns1.FeedType Type{get; set;}
}
and
public class ClassB : InterfaceA
{
public ns2.FeedType Type{get; set;}
}
this would not compile as InterfaceA would expect either ns1.FeedType or ns2.FeedType

Categories