How to serialize derived class to base class? - c#

I want to serialize object and pass it to method which parameter type is parent of object.
For example, I have this classes.
public class Base
{
public string TypeName => GetType().Name;
public string Data => JsonConvert.SerializeObject(this);
}
public class Derived : Base
{
public string Name { get; set; }
public int data1 { get; set; }
public int data2 { get; set; }
}
public class Derived2 : Base
{
...
}
....
I wrote the code as follows,
var obj = new Derived { Name = "John", data1 = 2000, data2 = 1500 };
Send(obj);
And Send(..) method is,
public void Send(Base info)
{
// Do Something with "info".
}
When I instantiate variable obj, program has fallen into infinite recursion because of "Data" in Base class.
How can I change the code?

Infinite recursion is caused by the Data property, which is serialized - that causes serialization of the this and the loop begins.
The best solution would be to simply change the property into method, which would not be serialized and would better serve the purpose. If you are dead set on property - you could just try marking the property with http://www.newtonsoft.com/json/help/html/PropertyJsonIgnore.htm which will cause it to be ignored during serialization.

Related

How to automatically include type information when serializing?

Is it possible to specify that I always want type-information in the json object when serializing a property in an class?
(Ideally with Newtonsoft).
I'm thinking something like this:
public abstract class Value {...}
public class BigValue : Value {...}
public class SmallValue : Value {...}
public class ValueContainer
{
[JsonSetting(TypenameHandling = TypenameHandling.All)] // <--- Something like this?
public Value TheValue { get; set; }
}
I am aware that I could specify this behavior when doing the parsing with a custom converter.
But I want to include the typeinformation every time objects of this type is serialized, without manually having to specify which serialization options to use.
Newtonsoft.Json's JsonPropertyAttribute has TypeNameHandling property which you can set:
public class Root
{
[JsonProperty(TypeNameHandling = TypeNameHandling.All)]
public Base Prop { get; set; }
}
public class Base
{
public int IntProp { get; set; }
}
public class Child:Base
{
}
// Example:
var result = JsonConvert.SerializeObject(new Root
{
Prop = new Child()
});
Console.WriteLine(result); // prints {"Prop":{"$type":"SOAnswers.TestTypeNamehandling+Child, SOAnswers","IntProp":0}}

Assign another type to specific type

I have base class thats inherited by multiple classes
public class Animal
{
public int Id { get; set; }
public static Animal Get(int id)
{
return ...
}
}
public class Cat : Animal
{
public CatPayload Payload { get; set; }
}
public class Dog : Animal
{
public DogPayload Payload { get; set; }
}
public class CatPayload
{
public int Lives { get; set; }
}
public class DogPayload
{
public bool IsDangerous { get; set; }
}
I store only Animal identifier in the database, so when I'm getting it using static method I get instance of Animal and I can't get strongly-typed instance of Payload. What would be the best way to implement this? Only thing I currently have on mind is checking type of animal instance if it matches any of the subclasses, but I'm not sure if there some simpler way?
I find your answer lacking a bit in details, so i don't know if this helps, but otherwise please extend your answer with more details, especially the parts of code that use these classes.
You can easily check with pattern matching:
// Creates a Cat, casts to an animal type
Animal myAnimal = new Cat(3);
if(myAnimal is Cat castedAnimalToCat)
{
// myCat is Animal returned true, which means we have a cat object.
// castedAnimalToCat is now a new variable of type Cat
Console.WriteLine($"{castedAnimalToCat.CatPayload}");
}
if(myAnimal is Dog castedAnimalToDog)
{
// ...
}
So somwhere in your database logic you can cast this to the right operand.
You can do some more fancier things with reflection, but your concern is always time safety and complexity with that.
1) Inheritance is supported in EF. This guide shows how it can be done:
Tutorial: Implement Inheritance with EF in an ASP.NET MVC 5 app
2) You might also consider using a flat structure for your data model in which the entities are referencing each other with foreign keys:
public class Cat
{
public int AnimalId { get; set; }
[ForeignKey(nameof(AccountId))]
public Animal Animal { get; set; }
public CatPayload Payload { get; set; }
}
3) For the payload you might use a string property for persistance containing json of the serialized class:
[NotMapped]
[JsonIgnore]
CatPayload Payload { get; set; }
public string PayloadJson
{
get => Payload == null ? null : JsonConvert.SerializeObject(Payload);
set => Payload = value == null ? null : JsonConvert.DeserializeObject<CatPayload>(value);
}

Default value of fields in derived classes

Is there a way i can have derived classes override the default value of the base class? In the example below i would need the Hammer.Name to return "Hammer".
public class ItemBase
{
public string Name = "Base";
}
public class Hammer: ItemBase
{
new public string Name = "Hammer";
}
public class Test
{
ItemBase MyThing = new Hammer();
// Prints "Base"
Console.WriteLine(ItemBase.Name);
}
You don't need different fields, you need different initializations of the same field.
class Base {
protected string name = "";
public Base() { name = "X"};
}
class Derived : Base {
public Derived() { name = "Y"}; //same {name } field of a Base class
}
You might consider using virtual properties instead of exposing public fields (which is considered bad practice).
As such, you can (with C# 6.0):
void Main()
{
ItemBase myThing = new Hammer();
// Doesn't print "Base"
Console.WriteLine(myThing.Name);
}
public class ItemBase
{
public virtual string Name { get; } = "Base";
}
public class Hammer : ItemBase
{
public override string Name { get; } = "Hammer";
}
or (if you're using older version of C#)...
public class ItemBase
{
public virtual string Name { get { return "Base"; } }
}
public class Hammer : ItemBase
{
public override string Name { get { return "Hammer"; } }
}
You are not defining a new default value in the derived type, you are declaring a completely new field that hides the field with the same name in the base class.
Because fields can't be virtual, the returned field is the one declared in the type through which you are invoking it.
Solution? Don't redeclare a new field, simply assign a new value to the existing field in the constructor of the derived type:
public class Hammer
{
public Hammer() {
Name = "Hammer"; }
}
Trying to figure out what exactly is needed while skating around the .NET version restrictions has been a headache but I have a solution. According to your comments you can use a constructor.
In that case this is really easy to do with properties (which are the preferred way to handle your situation) instead of public fields:
public class ItemBase
{
public ItemBase()
{
//When instantiating ItemBase the value of Name is "Base"
Name = "Base";
}
public string Name { get; set; }
}
public class Hammer : ItemBase
{
public Hammer()
{
//When instantiating Hammer the value of Name is "Hammer"
Name = "Hammer";
}
}
And to test just run this:
public class Program
{
public static void Main()
{
ItemBase itemBase = new Hammer();
Console.WriteLine(itemBase.Name);
itemBase.Name = "Foo";
Console.WriteLine(itemBase.Name);
}
}
Outputs:
Hammer
Foo
This should check off all the boxes. You now use properties (making your code better), each class has a default value, and the properties can be changed after instantiation.

Deserialize collection type virtual data member

I'm trying to deserialize json to RequestWithDefault object
JSON:
{
"fields":["f1","f2"]
}
My simple class diagram:
[DataContext]
public abstract class BaseRequest
{
[DataMember]
public virtual List<string> Fields { get; set; }
}
[DataContext]
public class RequestWithDefault : BaseRequest
{
[DataMember]
public override List<string> Fields {get; set; } = new List<string> {"test"}
}
After deserializing json to RequestWithDefault object Fields property contains ["test", "f1", "f1"]. I want to be sure that this default values are applied only in case when Fields were not specified in request, or was specified as null. How I can do this? I tried with [OnDeserializing] attribute but without success. Result is the same
According to this:
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/collection-types-in-data-contracts
Looks like during deserialization DataContractSerializer calling Add method from collection. That's why I have also default value and rest of items are added. When I will replace List<string> to string[] everything works fine.
It seems WCF serialization never use setter to set the value of the DataMember with type of collection, but use Add instead. Because of this, the only way to check whether the fields has any value is to check after it has been deserialized (not while deserializing).
[DataContext]
public abstract class BaseRequest
{
[DataMember]
public virtual List<string> Fields { get; set; }
}
[DataContext]
public class RequestWithDefault : BaseRequest
{
[System.Runtime.Serialization.OnDeserialized]
void OnDeserialized(System.Runtime.Serialization.StreamingContext c)
{
if (Fields == null
|| Fields.Count < 1)
{
Fields = new List<string> { "test" };
}
}
}

is it possible to set derived class default values without a constructor?

My goal is to make a static object that won't change, using a base class's member variables and abstract methods, as there will be multiple of these type of objects.
This is an example of what I want to do:
public abstract class BaseThing
{
public string Name { get; set; }
public string Description { get; set; }
public decimal Cost { get; set;}
public abstract void MethodThatDoesThings();
}
Then I want to have a derived object that has default values of those base variables, something like this (obviously doesn't work) :
public class DerivedThing : BaseThing
{
Name = "Name1";
Description = "Description1";
Cost = 1.00;
public override void MethodThatDoesThings()
{
//Actually does things
}
}
Is something like this possible without using a constructor? Not that I'm against using them, I'm just genuinely curious. Right now I feel as though my only option is to create many static classes that have the same properties.
No, you should implement a constructor for derived class to set default values. If you want to set default values, you can do it like this;
public class DerivedThing : BaseThing
{
public DerivedThing(string name = "Name", string description = "Description1", decimal cost = 1.0)
{
Name = name;
Description = description;
Cost = cost;
}
public override void MethodThatDoesThings()
{
}
}

Categories