I have a base class that implements a number of properties I want to store, and a derived class that contains additional properties that I don't want to store (they can be quite large). I have a container class that contains a list of the base class, which in turn contains a mixture of instances of the base class and its derived class. I create an XMLSerializer for the container class, but on serialization it complains that the derived class isn't included.
Is there any way of forcing the serializer to output base class XML only, irrespective of the instance type?
Note, I don't want to use XMLInclude, as I specifically don't want any of the properties in the derived class to be stored.
(Simplified example of the code)
public class MyBase {
public String Title { get; set; }
}
public class MyDerived : MyBase {
public String Details { get; set; }
}
public class Container {
private static XmlSerializer sSerializer = new XmlSerializer(typeof(Container));
public List<MyBase> mBases { get; set; }
public void MyProblem() {
mBases = new List<MyBase>();
mBases.Add(new MyBase { Title = "One" });
mBases.Add(new MyDerived { Title = "Two", Details = "An incredibly long string" });
using (var lWriter = XmlWriter.Create("C:\\Temp\\output.xml")) {
sSerializer.Serialize(lWriter, this);
}
}
}
Related
There is some logic that stores data of type DataContainer<BaseClass> using protobuf-net to some repository. Lets say I need to create new DerivedClass, so all new instances of DataContainer<DerivedClass> would be serialized properly, but for purposes of back-compatibility I want to have option to deserialize some previously saved data as DerivedClass without changing any data in repository.
class Program
{
static void Main(string[] args)
{
var baseObject = new BaseClass()
{
Name = "some name"
};
using (var stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize(stream, new DataContainer<BaseClass>{ Data = baseObject, DataBase64 = "base class Base64"});
stream.Seek(0, SeekOrigin.Begin);
//System.InvalidCastException: 'Unable to cast object of type 'BaseClass' to type 'DerivedClass'.'
var result = ProtoBuf.Serializer.Deserialize<DataContainer<DerivedClass>>(stream);
}
}
}
public interface IDataInterface
{
}
[ProtoBuf.ProtoContract]
[ProtoInclude(1001, typeof(DerivedClass))]
public class BaseClass: IDataInterface
{
[ProtoBuf.ProtoMember(1)]
public string Name { get; set; }
}
[ProtoBuf.ProtoContract]
public class DerivedClass : BaseClass
{
[ProtoBuf.ProtoMember(1)]
public int Index { get; set; }
}
public interface IContainerInterface
{
IDataInterface Content { get; }
}
[ProtoContract]
public class DataContainer<T> : IContainerInterface where T: IDataInterface
{
[ProtoMember(1)]
public T Data { get; set; }
[ProtoMember(2)]
public string DataBase64 { get; set; }
public IDataInterface Content => this.Data;
}
Is there any way to deserialize DataContainer<DerivedClass> out of serialized DataContainer<BaseClass>? The other way is to use some higher-level converter after deserializing DataContainer<BaseClass> that would simply turn it to DataContainer<DerivedClass>, but it involves some property-coping, which I would like to avoid as much as possible
If the item you're serializing *was a DerivedClass, then I would expect this to already work. However, if you need to deserialize existing data that only knew about BaseClass, then you would need to deserialize a DataContainer<BaseClass> and then test to find that Data is actually a DerivedClass.
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.
I have a class structure with a base abstract class with derived classes.
My base class looks like this:
public abstract class Graph
{
public Dictionary<Vector3, int> Dictionary { private set; get; }
public List<T> List { set; get; }
public Graph()
{
Dictionary = new Dictionary<Vector3,int>();
}
}
Then i have 2 derived classes:
public class Graph_Waypoints : Graph
{
public Graph_Waypoints() : base ()
{
List = new List<Waypoint>();
}
}
public class Graph_Tiles : Graph
{
public Graph_Tiles() : base ()
{
List = new List<Tile>();
}
}
But it seems i cannot do flexible types for my graph like this. The idea here is to create a graph that lets me use different objects for its nodes. Whether it be a way-point graph or a tile based graph.
But i am struggling to work out how to get the flexibility for it to allow me change the type when i create each new graph.
Am i going in the right direction here ? How can i get the type flexibility on my list?
You're nearly there; you're just missing some key parts:
public abstract class Graph<T> // <-- Graph must be generic
{
public Dictionary<Vector3, int> Dictionary { private set; get; }
public List<T> List { set; get; }
public Graph()
{
Dictionary = new Dictionary<Vector3, int>();
// You could initialize the list as you did before, but it's cleaner to do it here
List = new List<T>();
}
}
public class Graph_Waypoints : Graph<Waypoint> // <-- Subclasses must provide the base class generic arguments
{
public Graph_Waypoints() : base()
{
}
}
public class Graph_Tiles : Graph<Tile>
{
public Graph_Tiles() : base()
{
}
}
Also note, you do not need to explicitly write : base() when implementing argument-less constructors; the compiler does it for you
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.
.
Hello,
I have this sample code :
public class Vehicule
{
public string Name { get; set; }
public Brand Brand { get; set; }
}
public class Car : Vehicule
{
public string Matriculation { get; set; }
}
public class Brand
{
public string Name { get; set; }
}
public class Renault : Brand
{
public string Information { get; set; }
}
If I create this instance :
var car = new Car { Name = "Clio", Matriculation = "XXX-XXX", Brand = new Renault { Name = "Renault", Information = "Contact Infos" } };
When I serialize this object like that :
var serializer = new XmlSerializer(typeof(Car), new Type[] { typeof(Renault)});
serializer.Serialize(wr, car);
I obtain this :
<?xml version="1.0" encoding="utf-8"?>
<Car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Clio</Name>
<Brand xsi:type="Renault">
<Name>Renault</Name>
<Information>Contact Infos</Information>
</Brand>
<Matriculation>XXX-XXX</Matriculation>
</Car>
But, in my project, I don't have to have informations on derived classes, I would like only elements of base classes from this instance like this :
var serializer = new XmlSerializer(typeof(Vehicule));
serializer.Serialize(wr, car);
The Xml :
<?xml version="1.0" encoding="utf-8"?>
<Vehicule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Clio</Name>
<Brand>
<Name>Renault</Name>
</Brand>
</Vehicule>
Can you please, help me to obtain the good Xml (only with base type Vehicule and Brand) ?
Many thanks
You can't magically serialize a derived class as it's base because
"...Serialization checks type of instance by calling Object.getType()
method. This method always returns the exact type of object."
http://bytes.com/topic/net/answers/809946-how-force-serialize-base-type
The solution here, if you really need to only serialize the base class is to implement the IXmlSerializable interface and create your own custom serializer.
IXmlSerializable:
http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable(v=vs.110).aspx
One more thought. If you can work around the limitation of outputting the extra XML elements, you are able to serialize the derived class using only the base object by either 1) using XmlIncludeAttributes on the base class to tell it which types to expect or 2) using the XmlSerializer constructor overload that takes a list of types.
Edit:
After thinking about this a little more, a workaround would be that you would add a Clone() method onto your base object, then serialize the clone of the base.
LinqPad code:
public class Vehicule
{
public string Name { get; set; }
public Brand Brand { get; set; }
public Vehicule Clone()
{
return new Vehicule { Name = this.Name, Brand = new Brand { Name = this.Brand.Name } };
}
}
public class Car : Vehicule
{
public string Matriculation { get; set; }
}
public class Brand
{
public string Name { get; set; }
}
public class Renault : Brand
{
public string Information { get; set; }
}
void Main()
{
var car = new Car { Name = "Clio", Matriculation = "XXX-XXX", Brand = new Renault { Name = "Renault", Information = "Contact Infos" } };
var vehicle = car as Vehicule;
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(Vehicule));
XmlWriterSettings settings = new XmlWriterSettings
{
Encoding = new UnicodeEncoding(false, false),
Indent = false,
OmitXmlDeclaration = false
};
using(StringWriter textWriter = new StringWriter())
using(XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) {
serializer.Serialize(xmlWriter, vehicle.Clone());
textWriter.ToString().Dump();
}
}
This is one of the issues with inheritance, and another reason to favor composition imho.
I ran into the same issue on a mobile app where I had a Contact class that derives from ContactSummary. The repository returns Contact instances, but in lots of cases I only wanted the ContactSummary going over the wire to save on message sizes and data usage etc. The default Xml and Json serialisers would only work when the derived class was attributed with the [KnownType()] of the base class, but this still meant all those extra properties going over the wire.
Using inheritance it is problematic to achieve a viable solution, and I didn't want to resort to custom serialisers, and if the solution is to pollute the DTO with copy constructors and clone properties, then why not change the DTO to use composition instead?
If you have control over your DTOs, then restructuring them to use composition rather than inheritance may be the answer. In my example it was fairly simple...
public class ContactSummary
{
public string Name { get; set;}
public string Phone { get; set; }
}
public class Contact
{
public ContactSummary Summary { get; set; }
// ... other properties here
}
In your example, Car would need to contain a reference to Vehicle not inherit from it - something like...
[KnowTypes(typeof(Renault))]
public class Vehicle
{
public string Name { get; set; }
public Brand Brand { get; set; }
}
public class Car
{
public Vehicle Vehicle { get; set; }
public string Matriculation { get; set; }
}
Then when you want the 'base' type in your example, simply serialise Car.Vehicle.
I had the same problem and I got around it by re-mapping the inheriting class into the base class using AutoMapper:
MapperConfiguration config = new MapperConfiguration(cfg => cfg.CreateMap<Inheriting, Base>());
IMapper mapper = config.CreateMapper();
var baseObj = mapper.Map<Base>(InheritingObj);
There is not much you can customize on XmlSerializer out-of-the-box options.