Is there an shortcut way to deserialize protobuf base classes - c#

I am using protobuf-net version 2.3.2.0.
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
[ProtoInclude(12, typeof(SubClass))]
public class BaseClass
{
public string Id { get; set; }
public string Name { get; set; }
}
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class SubClass : BaseClass
{
public string PropertySub { get; set; }
}
As my base class is inherited from many child classes.
How can I avoid placing attribute [ProtoInclude] on my base classes so that props in my base class deserialize as expected.

Related

Cast concrete type to nested generic base type

Let's say I have nested generic data classes similar to the following:
public class BaseRecordList<TRecord, TUserInfo>
where TRecord : BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual IList<TRecord> Records { get; set; }
public virtual int Limit { get; set; }
}
public class BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
public class BaseUserInfo
{
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}
With 2 concrete versions like so:
// Project 1: Requires some extra properties
public class Project1RecordList : BaseRecordList<Project1Record, Project1UserInfo> {}
public class Project1Record : BaseRecord<Project1UserInfo>
{
public Guid Version { get; set; }
}
public class Project1UserInfo : BaseUserInfo
{
public string FavouriteFood { get; set; }
}
and
// Project 2: Some properties need alternate names for JSON serialization
public class Project2RecordList : BaseRecordList<Project2Record, Project2UserInfo>
{
[JsonProperty("allRecords")]
public override IList<Project2Record> Records { get; set; }
}
public class Project2Record : BaseRecord<Project2UserInfo> {}
public class Project2UserInfo : BaseUserInfo
{
[JsonProperty("username")]
public override string Name { get; set; }
}
I'm then happy to have 2 repositories that return Project1RecordList and Project2RecordList respectively, but at some point in my code I find myself needing to be able to handle both of these in one place. I figure that at this point I need to be able to treat both of these types as
BaseRecordList<BaseRecord<BaseUserInfo>, BaseUserInfo>
as this is the minimum required to meet the generic constraints, but trying to cast or use "as" throws up errors about not being able to convert.
Is there any way to do this, or even a more sane way to handle this situation without massive amounts of code duplication? If it makes any difference this is for a web app and there are already a large number of data classes, many of which use these nested generics.
What you are talking about is called covariance and MSDN has a great article on this here: https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
First, create a new interface:
interface IBaseRecord<out TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecord inherit from the new interface:
public class BaseRecord<TUserInfo> : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
If done right, this should compile:
IBaseRecord<BaseUserInfo> project1 = new Project1Record();
IBaseRecord<BaseUserInfo> project2 = new Project2Record();
To expand this to the BaseRecordList, create IBaseRecordList:
interface IBaseRecordList<out TRecord, out TUserInfo>
where TRecord : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecordList inherit from that:
public class BaseRecordList<TRecord, TUserInfo> : IBaseRecordList<TRecord, TUserInfo>
And then use as such:
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project1 = new Project1RecordList();
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project2 = new Project2RecordList();
Once you have that setup, just add whatever properties or functions you need to use generically to the interfaces.

Inheritance of abstract class use another Type than defined for property

I created the following abstract class:
public abstract class AbstractClass
{
public abstract string Name { get; set; }
public abstract object Value { get; set; }
}
Now I want to derive two classes of the abstract class. I want to use an enum instead of the type object. My derived classes look like this:
First class:
public class InheritanceClass1:AbstractClass
{
public override string Name { get; set; }
public override FirstEnum Value { get; set; }
}
Second class:
public class InheritanceClass2 : AbstractClass
{
public override string Name { get; set; }
public override SecondEnum Value { get; set; }
}
I'm getting an error showed in my code, that the type of the property Value isn't object. I tryed to use the new-keyword instead of override like this:
In my abstract class:
public object Value { get; set; }
In my derived class:
public new FirstEnum Value { get; set; }
But if I create a List<AbstractClass> I have the problem that I can't use it for example for Linq because I would retrieve the "wrong" property. It is just hided, but still there, so I have to override the property.
So how do I have to change my abstract class and my derived classes, that I can use different types in my derived classes?
You can use abstract class like this:
public abstract class AbstractClass<T>
{
public abstract string Name { get; set; }
public abstract T Value { get; set; }
}
And derived class will change like this:
public class InheritanceClass1 : AbstractClass<FirstEnum>
{
public override string Name { get; set; }
public override FirstEnum Value { get; set; }
}
If you know that you will need only enums, you can add struct, IConvertible restriction to T:
public abstract class AbstractClass<T> where T : struct, IConvertible
{
public abstract string Name { get; set; }
public abstract T Value { get; set; }
}
Update based on comment:
Not the cleanest solution if you need List<AbstractClass>, but you can have additional class:
public abstract class AbstractClass
{
public abstract string Name { get; set; }
public abstract int GetValue ();
}
Which will then be inherited by AbstractClass<T>:
public abstract class AbstractClass<T> : AbstractClass where T : struct, IConvertible
{
public abstract T Value { get; set; }
}
And InheritancClass:
public class InheritanceClass1 : AbstractClass<FirstEnum>
{
public override string Name { get; set; }
public override FirstEnum Value { get; set; }
public override int GetValue () => (int)Value;
}
And then you can use it in a list:
var list = new List<AbstractClass> { new InheritanceClass1 (), new InheritanceClass2 () };
In this way you can use List<AbstractClass> with GetValue method. If you are using only enums you can always recast it to enum value. Ofcorse, you would not know exactly which enum it is, but you can add additional field for that.

How to force implementation of an abstract classes members in the inheriting class?

I have the following two classes:
abstract class LogItem {
public String payload { get; set; }
public String serverId { get; set; }
public DateTime timeRecieved { get; set; }
}
class MyLogItem : LogItem
{
//No I want this to have to have the members from the abstract class above, as if it where an interface?
}
So in other words I am wanting a type if interface that can have definitions or variables which all classes that implement it have to have, but they could add more if they required ?
The above example builds, even if i dono add the members from the abstract class.
edit
Forget what I've said before. These are attributes, not methods. For them to be accessible on derived classes, you make them protected or public. The difference is that public members are visible to the world, while protected ones are visible to the class and subclasses.
Any class derived from your LogItem may have other variables.
abstract class LogItem {
public String payload { get; set; }
public String serverId { get; set; }
public DateTime timeRecieved { get; set; }
}
class MyLogItem : LogItem
{
//No I want this to have to have the members from the abstract class above, as if it where an interface?
private void TestMethod(){
String test = payload;
}
}
check out this post for more information
Your MyLogItem class can reference any of the above members directly. They are accessible
You may declare an interface with those
public interface MyInterface {
public String payload { get; set; }
public String serverId { get; set; }
public DateTime timeRecieved { get; set; }
}
and your class
public class MyLogItem : MyInterface
{
String _payload;
public String payload { get{ return _payload; } set {_payload=value;} }
...
}
The abstract keyword can also be applied to methods, as described here.

Xml-attributes in interfaces and abstract classes

I found something that confused me today:
1. If I have this:
public interface INamed
{
[XmlAttribute]
string Name { get; set; }
}
public class Named : INamed
{
public string Name { get; set; }
}
It gives the following output (Name property serialized as element):
<Named>
<Name>Johan</Name>
</Named>
2. If I have this:
public abstract class NamedBase
{
[XmlAttribute]
public abstract string Name { get; set; }
}
public class NamedDerived : NamedBase
{
public override string Name { get; set; }
}
The XmlSerializer throws System.InvalidOperationException
Member 'NamedDerived.Name' hides inherited member 'NamedBase.Name',
but has different custom attributes.
The code I used for serialization:
[TestFixture]
public class XmlAttributeTest
{
[Test]
public void SerializeTest()
{
var named = new NamedDerived {Name = "Johan"};
var xmlSerializer = new XmlSerializer(named.GetType());
var stringBuilder = new StringBuilder();
using (var stringWriter = new StringWriter(stringBuilder))
{
xmlSerializer.Serialize(stringWriter, named);
}
Console.WriteLine(stringBuilder.ToString());
}
}
My question is:
Am I doing it wrong and if so what is the correct way to use xml attributes in interfaces and abstract classes?
Attributes are not inherited on overriden properties. You need to redeclare them.
Also in your first example the behavior is not the "expected" one as you declared XmlAttribute at the interface level and yet the serialized xml contains the value as an element. So the attribute in the interface is ignored and only info taken from the actual class matters.
I think you should xmlignore your abstract class property
public abstract class NamedBase
{
[XmlIgnore]
public abstract string Name { get; set; }
}
public class NamedDerived : NamedBase
{
[XmlAttribute]
public override string Name { get; set; }
}

Have properties of base class serialize when serializing derived class

I have a base class with a few properties:
// must include any derived classes here as known types or else they will throw errors on serialization
[KnownType(typeof(CollaborationEventMeasureDistance))]
[DataContract]
public partial class CollaborationEvent
{
public bool HasBeenTransported { get; set; }
public Guid MessageBoxGuid { get; set; }
public CollaborationEvent()
{
HasBeenTransported = false;
}
}
And a derived class with some properties of its own:
public class CollaborationEventMeasureDistance : CollaborationEvent
{
public Geometry Geometry { get; set; }
}
When I serialize the derived class, all of its properties are serialized, but the properties it inherits from the base class are not:
<CollaborationEvent i:type="CollaborationEventMeasureDistance">
<Geometry xmlns:d4p1="http://schemas.datacontract.org/2004/07/ESRI.ArcGIS.Client.Geometry"
i:type="d4p1:Polyline">
<d4p1:spatialReference>
<d4p1:wkid>26910</d4p1:wkid>
</d4p1:spatialReference>
<d4p1:paths>
<d4p1:points>
<d4p1:point>
<d4p1:spatialReference>
<d4p1:wkid>26910</d4p1:wkid>
</d4p1:spatialReference>
<d4p1:x>460892.23924271885</d4p1:x>
<d4p1:y>5367682.5572773879</d4p1:y>
</d4p1:point>
<d4p1:point>
<d4p1:spatialReference i:nil="true" />
<d4p1:x>461001.35841108358</d4p1:x>
<d4p1:y>5367648.5755294543</d4p1:y>
</d4p1:point>
</d4p1:points>
</d4p1:paths>
</Geometry>
</CollaborationEvent>
Can anyone point out what I am doing wrong?
I expect my XML to look more like:
<CollaborationEvent i:type="CollaborationEventMeasureDistance">
<HasBeenTransported>True</HasBeenTransported>
<MessageBoxGuid>blah</MessageBoxGuid>
<Geometry xmlns:d4p1="http://schemas.datacontract.org/2004/07/ESRI.ArcGIS.Client.Geometry"
i:type="d4p1:Polyline">
<d4p1:spatialReference>
<d4p1:wkid>26910</d4p1:wkid>
</d4p1:spatialReference>
<d4p1:paths>
<d4p1:points>
<d4p1:point>
<d4p1:spatialReference>
<d4p1:wkid>26910</d4p1:wkid>
</d4p1:spatialReference>
<d4p1:x>460892.23924271885</d4p1:x>
<d4p1:y>5367682.5572773879</d4p1:y>
</d4p1:point>
<d4p1:point>
<d4p1:spatialReference i:nil="true" />
<d4p1:x>461001.35841108358</d4p1:x>
<d4p1:y>5367648.5755294543</d4p1:y>
</d4p1:point>
</d4p1:points>
</d4p1:paths>
</Geometry>
</CollaborationEvent>
Thanks
Assuming your Geometry class is serializable, try something like this:
[DataContract, Serializable]
public class CollaborationEventMeasureDistance : CollaborationEvent
{
[DataMember]
public Geometry Geometry { get; set; }
}
[KnownType(typeof(CollaborationEventMeasureDistance))]
[DataContract, Serializable]
public partial class CollaborationEvent
{
[DataMember]
public bool HasBeenTransported { get; set; }
[DataMember]
public Guid MessageBoxGuid { get; set; }
public CollaborationEvent()
{
HasBeenTransported = false;
}
}

Categories