Going on with my quest to bend protobuf-net to my own will..
I've seen a few questions around SO on how to add sub-classes dynamically
for the serializer to be able to encode the sub-class.., like this or this
My situation is bit different, I have a base class that might get sub-classed in late-bounded code, and I want to serialize is as the BASE class, and completely ignore the sub-class's fields/properties.
The reason I need this, is that later on, when I deserialize the data, the sub-class's code will not be even available, so constructing the sub-class will not be even possible.
Is there a way to limit/prohibit sub-class serializtion?
In my case I have a List where some items in the list are DerivedClass.
I would like to find a way to make protobuf-net serialize everything as BaseClass and to deserialize to BaseClass as well...
I've tried peering into code, but haven't found something too useful.
Normally, the library is very particular about spotting derived classes - and treating them differently from the base class. The only current exception to that is proxy classes, in particular Entity Framework and NHibernate. For a tidy solution, it would seem practical to add some kind of "ignore subclasses" switch. But while that doesn't exist, a very lazy (and hacky) approach would be to cheat using the existing handling for NHibernate, for example:
namespace NHibernate.Proxy {
interface INHibernateProxy {}
}
...
public class SomeDerivedType : BaseType, INHibernateProxy {}
this will then automatically be serialized as per BaseType. It does have a faint whiff of cheating about it, though.
Related
Given a .proto that looks like this:
message Base {
string Dummy = 1
}
message Derived {
Base Super = 1
string Parp = 2
}
... And some C# something like:
public class Base {
public string Dummy;
}
public class Derived : Base {
public string Parp
}
How would one go about customising the serialisation in protobuf-net to be able to do this? Initially I started looking at using a TypeModel and calling AddSubType for the Base MetaType, but then it seems it creates type definitions for Base with optional fields of all the derived classes (ie the other way around vs what I require)
I thought I might be able to walk the hierarchy myself but looking at TypeModel, it seems to support supplying the type to Deserialise but it uses value.GetType() during serialisation. Even then it wasn't entirely clear how I might do this. Is my only option to use ProtoWriter to write every field by hand? This is what I am currently attempting, but I hoped there was an easier way.
The first thing to note is that protobuf itself does not support inheritance. There is no "official" layout for this. Protobuf-net will not support serialization the way you desire: the choice of sub-type encapsulation (rather than base-type encapsulation) was made to fix several issues, including:
satisfying the Liskov substitution principle
working correctly when the serialized type was not known/expected by the caller
basically, working at all (for anything other than the simplest example where both ends know in advance exactly which sub-type they are getting, which largely defeats the point of polymorphism)
However, you could map the DTO manually, basically so your DTO layer doesn't use inheritance at all.
I have a bunch of xml serialized objects in a database.
But, I refactored and renamed the classes involved, so deserializing from the db is difficult.
I thought that by adding the term [XmlRoot("DB_Class_Name")] atop the renamed classes would fix the issue, but it doesn't appear to.
Is there a way to fix the issue using labels like [XmlRoot], [XmlElement] etc., without renaming my classes to their old classnames, and without writing a special deserialize function?
Also, are there any good sources on what is happening under the hood when using xmldeserializaiton and labels like [XmlRoot]?
First of all, [XmlRoot] etc. aren't labels, they're attributes.
Second, [XmlRoot] only affects the class when that class is used as the root element of the document. It has no affect when an instance of that class is used as a child or other descendant.
Use [XmlType] on the class, or [XmlElement] on a property that is of the type of the class.
I have a very odd exception in my C# app: when trying to deserialize a class containing a generic List<IListMember> (where list entries are specified by an interface), an exception is thrown reporting that "the type ...IListMember is not marked with the serializable attribute" (phrasing may be slightly different, my VisualStudio is not in English).
Now, interfaces cannot be Serializable; the class actually contained in the list, implementing IListMember, is [Serializable]; and yes, I have checked that IListMember is in fact defined as an interface and not accidentally as a class!
I have tried reproducing the exception in a separate test project only containing the class containing the List and the members, but there it serializes and deserializes happily :/
Does anyone have any good ideas about what it could be?
Edit:
We are using a BinarySerializer; and I repeat, when extracted to a test project the class serializes happily. So I do not need a workaround to serialize a class containing a List<IThing>, as in general this works fine (as long as the actual classes implementing IThing are serializable); what I am looking for is reasons why it might not work this particular time...
I have now put in a workaround (serializing each list member individually, together with the number of entries, and recreating the List by hand), but would really like to find out what it could be for future reference.
It doesn't matter that the class backing the interface is serializable. Interfaces cannot be serialized, period.
In order to deserialize, the serializer needs to be able to instantiate a concrete type, and it determines this type by reflecting on the fields/properties of the class-to-be-deserialized.
If the type of one of those properties is an interface, then it will never be able to construct a concrete type to assign to that member. All it sees is the interface, it has no idea which class originally implemented it when the data was serialized.
If you want the class to be serializable, then every class in the object graph must be a concrete type. No interfaces allowed.
(Postscript: Actually, I sort of lied, the BinaryFormatter can serialize/deserialize directly to/from interface types, but I strongly suspect that's not what's being used here.)
The Easy Way (Although ugly) Wrap your list:
public ListMemberCollection : List<IListMember>, ISerializable
{
// Implement ISerializable Here
}
The Alternative Way (Although better) AbstractBaseClass:
[Serializable]
public ListMemberBase : IListMember
{
// Implement abstract versions of everything
}
A possibility?: (On your other class)
class TheClassYoureSerializing
{
[Serializable]
public List<IListMember> list { get; set; }
}
Seeing as you can convert any document to a byte array and save it to disk, and then rebuild the file to its original form (as long as you have meta data for its filename etc.).
Why do you have to mark a class with [Serializable] etc? Is that just the same idea, "meta data" type information so when you cast the object to its class things are mapped properly?
Binary serialization is pretty powerful, it can create an instance of a class without running the constructor and can set fields in your class that you declared private. Regular code can of course not do this. By applying the [Serializable] attribute, you explicitly give it the go-ahead to mess with your private parts. And you implicitly give that permission to only the BinaryFormatter class.
XML serialization doesn't need this kind of okay, it only serializes members that are public.
DataContractSerializer can serialize private members as well. It therefore needs an explicit okay again, now with the [DataContract] attribute.
First off, you don't have to.
It is simply a marker interface that tells the serializer that the class is composed of items that it can serialize (which may or may not be true) and that is can use the default serialization.
The XMLSerializer has the additional requirement to have a zero parameter constructor to the class.
There are other serializers that use contracts for serialization (such as the DataContractSerializer) - they give you more control over serialization than simply marking a class as Serializable. You can also get more control by implementing the ISerializable interface.
It's basically metadata that indicates that a class can be serialized, nothing more.
It is required by a lot of framework serializers, which refuse to deal with types not having this attribute applied to them.
Serialization can create security holes and may be plagued by versioning problems. On top of that, for some classes, the very idea of serialization is outright nonsense.
For details, see the excellent answers to Why Java needs Serializable interface?, especially this one, this one, and this one. They make the case that serialization should be a feature you have to explicitly opt into.
For a counterpoint, the accepted answer to that question makes the case that classes should be serializable by default.
It indicates to the serializer that you want that class to be serialized as you may not want all properties or classes to be serialized.
I see it as a reminder that I will allow the class to be serialized. So you don't implicitly serialize something you shouldn't.
Don't know it that is designers' intention.
BTW, I just love BinaryFormatter and use it as much as I can. It handles pretty much of the stuff automatically (like rebuilding complex object graphs with recurring references spread throughout the graph).
I'm using the DataContractSerializer to serialize an objects properties and fields marked with DataMember attributes to xml.
Now a have another use case for the same class, where I need to serialize other properties and other fields.
Are there a way to add "another DataMemberAttribute" that can be used for my other serialization scenario?
No, basically.
If you want to use the existing DataContractSerializer, you'll have to maintain a second version of the DTO class and convert the data between them.
Options if you are writing your own serialization code:
declare your own [DataMember]-style attribute(s) and interpret them at runtime in your own serialization code
use a "buddy class"
use external metadata (such as a file)
use code-based configuration (i.e. via a DSL)
In reality, I expect the first will be the simplest choice.
In a similar scenario in the past, we've taken an Object Oriented approach, and created a new class that extends from the main class.
To help you achieve inhertience with the DataContractSerializer, check out KnownTypeAttribute
In one of your comments to your question,
If the same class is implementing multiple interfaces, certain data elements may be relevant to only one of the interfaces.
If that is the case in your scenario, then perhaps your Data Service Contracts should be exposing just the Interfaces, and not the Class?
For example, if you have a class like:
[DataContract]
public class DataObject : IRed, IBlue
then rather than have your operation contract expose DataObject, you have two operation contracts one for IRed and one for IBlue.
This eliminates the need for custom serialization code.
There is a way to do it, but it's an ugly hack.
The DataContractSerializer can serialize objects that implement the IXmlSerializable interface. You could implement the interface and create your own ReadXml(XmlReader reader) and WriteXml(XmlWriter writer) methods that could serialize the object in different ways.
Note that you'd have to have a flag embedded within the class itself to determine which way to serialize the object. (There's no way to tell the DataContractSerializer which mode to use, so the flag has to be contained in the object itself.)
A second version of the DTO class, as #Marc suggests, would be much cleaner.