I when I run the shown unit test I get an exception Object does not match target type during protobuf deserialization.
I narrowed the problem down to the default constructor ContainerForA().
This default constructor initializes the variable PropA with an instance of ClassA during deserialization, as protobuf-net will call the default constructor. Afterwards the protobuf deserializer should overwrite this property with the serialized instance of ClassB. I think at this point the exception is thrown.
If I remove the code from the default constructor ContainerForA() the test seems to work.
Does protobuf-net have constraints on what you are allowed to do in your default constructor? Or is there any other problem with my code?
I am using protobuf-net portable 2.0.0.668
[ProtoContract]
[ProtoInclude(101, typeof(IBaseB))]
[ProtoInclude(102, typeof(ClassA))]
public interface IBaseA { }
[ProtoContract]
[ProtoInclude(103, typeof(ClassB))]
public interface IBaseB : IBaseA { }
[ProtoContract]
public class ClassA : IBaseA
{
[ProtoMember(1)]
public int PropA { get; set; }
}
[ProtoContract]
public class ClassB : IBaseB
{
[ProtoMember(2)]
public string PropB { get; set; }
}
[ProtoContract]
public class ContainerForA
{
[ProtoMember(3)]
public IBaseA InstanceOfA { get; set; }
public ContainerForA()
{
InstanceOfA = new ClassA();
}
}
[TestClass]
public class ProtoTestBed1
{
[TestMethod]
public void TestProto()
{
var containerForA = new ContainerForA()
{
InstanceOfA = new ClassB { PropB = "I'm B"}
};
var proto = new ProtobufSerializer();
var bytes = proto.Serialize(containerForA);
var containerForADeserialized = proto.Deserialize<ContainerForA>(bytes);
Debug.WriteLine(containerForADeserialized);
}
}
I'm not sure what the constraints are (Mark will probably come along in a bit and tell you) but there are work arounds
Try this:
[ProtoContract(SkipConstructor=true)]
public class ContainerForA
{
[ProtoMember(3)]
public IBaseA InstanceOfA { get; set; }
public ContainerForA()
{
InstanceOfA = new ClassA();
}
}
Had another look at this using the Portable version. Not sure how to do this using attributes but came up with the following solution which seemed to work by using a factory method to "undo" the constructor.
public class ContainerForA
{
public IBaseA InstanceOfA { get; set; }
public ContainerForA()
{
InstanceOfA = new ClassA();
}
private static ContainerForA EmptyContainerFactory()
{
return new ContainerForA()
{
InstanceOfA = null
};
}
}
static void Main(string[] args)
{
var containerForA = new ContainerForA()
{
InstanceOfA = new ClassB { PropB = "I'm B" }
};
var model = RuntimeTypeModel.Create();
var baseA = model.Add(typeof(IBaseA), true);
baseA.AddSubType(101, typeof(IBaseB));
baseA.AddSubType(102, typeof(ClassA));
var baseB = model.Add(typeof(IBaseB), true);
baseB.AddSubType(103, typeof(ClassB));
var classA = model.Add(typeof(ClassA), true);
classA.AddField(1, "PropA");
var classB = model.Add(typeof(ClassB), true);
classB.AddField(2, "PropB");
var container = model.Add(typeof(ContainerForA), true);
container.AddField(3, "InstanceOfA");
container.SetFactory("EmptyContainerFactory");
MemoryStream mem = new MemoryStream();
model.Serialize(mem, containerForA);
mem.Seek(0, SeekOrigin.Begin);
var containerForADeserialized = model.Deserialize(mem, null, typeof(ContainerForA));
Debug.WriteLine(containerForADeserialized);
}
The project maintainer Marc Gravell confirmed that this is intended behavior:
Protobuf-net prefers not to replace instances;
If a sub-object / collection / etc is non-null, it tries very hard to use it rather than reallocate everything in the tree.
In your case, the simplest fix is probably to simply not run the constructor during deserialization
Related
To cut down on reused code throughout my repository which gets values from another library, I wanted to create extension methods for "parsing"(for lack of a better word) one class to another. How do I implement abstract methods with different parameters.
I can't find anything that answers my question, and I'm not sure it can even be done.
Instead of having something like this in multiple places.
var list = _library.GetList();
var model = list.Select(o => new ClassA()
{
ID = o.ID,
Name = o.Name
}).ToList<ClassA>();
I want extension methods so I can call something like
var list = _library.GetList();
var model = ExtensionClass.ParseMany(list);
But, I want to base this off an abstract class so it can be reused by mutliple different classes, so I have
public abstract class Parser<U, T> where T : class where U : class
{
public abstract T ParseOne(U parser);
public abstract IEnumerable<T> ParseMany(IEnumerable<U> parser);
}
public class ParseA<ClassA, ClassADTO>
{
public override ClassA ParseOne(ClassADTO parser){ // }
}
But it doesn't seem that my parameter that is passed in is the actual object, it says it's a KeyValuePair and now I'm lost.
I expect to able to return a new instance based on my parameter, basically what I already do in my code multiple times.
I guess you can have a generic parser using Func. I just wrote a sample and hope it helps you.
public class ClassA
{
public int SomeNumber { get; set; }
public string SomeString { get; set; }
}
public class ClassB
{
public int OtherNumber { get; set; }
public string OtherString { get; set; }
}
public static class ExecuteParsingFunction
{
public static TDestiny Parse<TOrigin, TDestiny>(TOrigin origin,
Func<TOrigin, TDestiny> parserFunction)
{
return parserFunction(origin);
}
}
public static class ParsingFunctions
{
public static ClassB ParseAToB(ClassA a)
{
return new ClassB { OtherNumber = a.SomeNumber, OtherString = a.SomeString };
}
public static IEnumerable<ClassB> ParseManyAToB(IEnumerable<ClassA> aCollection)
{
foreach(var a in aCollection)
yield return ParseAToB(a);
}
}
public void Sample()
{
var a = new ClassA { SomeNumber = 1, SomeString = "Test" };
var manyAs = new List<ClassA> { a };
var b = ExecuteParsingFunction.Parse(a, ParserFunctions.ParseAToB);
var manyBs = ExecuteParsingFunction.Parse(manyAs, ParserFunctions.ParseManyAToB);
}
I'm trying to serialize the IAnimal instance object to json using Json.NET.
Class structure:
public class Dog : IAnimal {
public int Width { get; set; }
public double Bark { get; set; }
}
public class Cat : IAnimal {
public int Width { get; set; }
public double Meow { get; set; }
}
public interface IAnimal {
int Width { get; set; }
}
public class AnimalContainer {
public IAnimal Animal { get; set; }
}
Tried this way (please notice I use 'TypeNameHandling.Auto' as I found in other threads):
public void IAnimal_ShouldBeJsonSerializable() {
var animal = new AnimalContainer {Animal = new Dog {Bark = 5, Width = 2}};
var json = JsonConvert.SerializeObject(animal,
new JsonSerializerSettings{TypeNameHandling = TypeNameHandling.Auto});
var deserializedAnimal = JsonConvert.DeserializeObject<AnimalContainer>(json);
}
but is throwing me exception that "Could not create an instance of type IAnimal, Type is an interface or abstract class and cannot be instantiated".
But the json contains the concrete type information!
How can I make it work?
It does not look like you are passing the serializer settings to your DeserializeObject call. You need to include the TypeNameHandling both on Serialize and Deserialize.
var animal = new AnimalContainer { Animal = new Dog { Bark = 5, Width = 2 } };
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto };
var json = JsonConvert.SerializeObject(animal, settings);
var deserializedAnimal = JsonConvert.DeserializeObject<AnimalContainer>(json, settings);
Console.WriteLine(deserializedAnimal.Animal.GetType().Name);
I am trying to use MessagePack to serialize an object that has a property of an interface type. When I call Pack, it throws SerializationException that says a serializer is not defined for the interface.
Code example:
namespace ConsoleApplication1
{
// interfaces and classes declaration
public interface IDummyInterface { }
public class DummyObject : IDummyInterface
{
public string Value { get; set; }
}
public class SmartObject
{
public string Name { get; set; }
IDummyInterface DummyOne { get; set; }
}
// in main
var mySmartObject = new SmartObject() { Name = "Yosy", DummyOne = new DummyObject() { Value = "Value"} };
using(var stream = new MemoryStream())
{
var serializer = MessagePackSerializer.Create<SmartObject>();
serializer.Pack(mySmartObject, stream); // => This code throws the exception
}
}
Can I tell MessagePack which serializer to use for IDummyInterface and tell it to act as DummyObject?
It seems to me you are using msgpack-cli. To make it work, basically there are two ways to do it.
1. Use MessagePackKnownTypeAttribute
This one is easy and straightforward.
public class SmartObject
{
public string Name { get; set; }
[MessagePackKnownType("d", typeof(DummyObject))]
public IDummyInterface DummyOne { get; set; } // Need to make this property public
}
2. Implement custom serializer
If you want a clean model class without reference to MsgPack library, you can do the following, but you need to figure out a way to serialize/deserialize SmartObject (efficiently).
public class SmartObjectSerializer : MessagePackSerializer<SmartObject>
{
public SmartObjectSerializer(SerializationContext ownerContext) : base(ownerContext)
{
}
protected override void PackToCore(Packer packer, SmartObject objectTree)
{
var str = ToString(objectTree); // TODO: Just an example
packer.Pack(str);
}
protected override SmartObject UnpackFromCore(Unpacker unpacker)
{
var str = unpacker.LastReadData.AsStringUtf8(); // TODO: Just an example
return new SmartObject
{
// TODO: Initialize based on str value
};
}
}
// In main
var context = new SerializationContext();
context.Serializers.RegisterOverride(new SmartObjectSerializer(context));
var serializer = MessagePackSerializer.Get<SmartObject>(context);
// The rest is the same
There are some sample codes you may be interested to take a look.
CustomSerializer
Polymorphism
Actually, I want to access properties of a base class in a method and I am not instantiating that object directly. Below is code, I am working on:
public class Test
{
public static void Main()
{
drivedclass obj = new drivedclass();
obj.DoSomething();
}
}
public class drivedclass : baseclass
{
public void DoSomething()
{
LoadSomeThing();
}
}
public class baseclass
{
public string property1
{
get;
set;
}
public string property2
{
get;
set;
}
public void LoadSomeThing()
{
//here I want to access values of all properties
}
}
I would like to know if there is a way, I can access the properties in method of same class and that class is base class.
You can just use property1 and property2 as they are.
However, note that in LoadSomeThing() you will not be able to access any properties of drivedlcass, because base classes cannot see properties of their derived classes by definition.
You can access them with reflection, but this is not the 'normal' way.
foreach(PropertyInfo prop in this.GetType().GetProperties())
{
prop.SetValue(this, newValue);
}
If you want to make it 'cleaner', you should make the properties virtual.
Use the following method to enumerate all property values:
public void EnumerateProperties()
{
var propertiesInfo = this.GetType().GetProperties();
foreach (var propertyInfo in propertiesInfo)
{
var val = propertyInfo.GetValue(this, null);
}
}
Question is quiet unclear but if you wish to access your properties, they are well present in both the Base class and the derived class. thus, if you do s = obj.property2 in your main class Test, that should be available.
public class Test {
public static void Main( ) {
drivedclass obj = new drivedclass( );
obj.DoSomething( );
string s = obj.property2 ;
}
}
You could always make it explicit:
public class DerivedClass : BaseClass
{
public string Property3
{ get; set; }
public void DoSomething ()
{
LoadSomeThing();
}
public override void LoadSomeThing ()
{
base.LoadSomeThing();
Console.WriteLine(Property3);
}
}
public class BaseClass {
public string Property1
{ get; set; }
public string Property2
{ get; set; }
public virtual void LoadSomeThing()
{
Console.WriteLine(Property1);
Console.WriteLine(Property2);
}
}
You can simply try: this.property1
I am trying to learn how to create generic classes with c#. Can someone explain why I get a compile error when I run this program.
I have created the IZooAnimal interface. All zoo animals will implement this interface.
public interface IZooAnimal
{
string Id { get; set; }
}
public class Lion : IZooAnimal
{
string Id { get; set; }
}
public class Zebra : IZooAnimal
{
public string Id { get; set; }
}
The ZooCage will hold animals of the same Type
public class ZooCage<T> where T : IZooAnimal
{
public IList<T> Animals { get; set; }
}
The zoo class have cages
public class Zoo
{
public IList<ZooCage<IZooAnimal>> ZooCages { get; set; }
}
The program that uses the classes
class Program
{
static void Main(string[] args)
{
var lion = new Lion();
var lionCage = new ZooCage<Lion>();
lionCage.Animals = new List<Lion>();
lionCage.Animals.Add(lion);
var zebra = new Zebra();
var zebraCage = new ZooCage<Zebra>();
zebraCage.Animals = new List<Zebra>();
zebraCage.Animals.Add(zebra);
var zoo = new Zoo();
zoo.ZooCages = new List<ZooCage<IZooAnimal>>();
zoo.ZooCages.Add(lionCage);
}
}
When I compile I get the following error:
Error 2 Argument 1: cannot convert from 'ConsoleApplication2.ZooCage<ConsoleApplication2.Lion>' to 'ConsoleApplication2.ZooCage<ConsoleApplication2.IZooAnimal>'
What changes do I have to do in order to make my program run?
#DanielMann's answer is quite good, but suffers from one drawback: the original IList interface cannot be used with the ICage interface. Instead, the ICage has to expose a ReadOnlyCollection, and expose a new method called CageAnimal.
I've also re-written the code using a similar approach. My ICage implementation is much weaker, but it allows you to stick with IList semantics inside.
public interface IZooAnimal
{
string Id { get; set; }
}
public class Lion : IZooAnimal
{
public string Id { get; set; }
}
public class Zebra : IZooAnimal
{
public string Id { get; set; }
}
public interface ICage
{
IEnumerable<IZooAnimal> WeaklyTypedAnimals { get; }
}
public class Cage<T> : ICage where T : IZooAnimal
{
public IList<T> Animals { get; set; }
public IEnumerable<IZooAnimal> WeaklyTypedAnimals
{
get { return (IEnumerable<IZooAnimal>) Animals; }
}
}
public class Zoo
{
public IList<ICage> ZooCages { get; set; }
}
class Program
{
static void Main(string[] args)
{
var lion = new Lion();
var lionCage = new Cage<Lion>();
lionCage.Animals = new List<Lion>();
lionCage.Animals.Add(lion);
var zebra = new Zebra();
var zebraCage = new Cage<Zebra>();
zebraCage.Animals = new List<Zebra>();
zebraCage.Animals.Add(zebra);
var zoo = new Zoo();
zoo.ZooCages = new List<ICage>();
zoo.ZooCages.Add(lionCage);
}
}
Since you want to have multiple cages, but each type of cage can only hold one animal, your model is slightly off.
I rewrote the code as follows:
IZooAnimal is unchanged.
There's a covariant interface ICage that accepts any type of IZooAnimal. That allows you to have a strongly-typed cage for every type of animal.
Then, I have a Cage concrete implementation of ICage. Cage is generic, but you could just as easily make it an abstract class and then make animal-specific cage implementations. For example, if your zebra needs to be fed grass, and your lion needs to be fed meat, you could specialize the implementations of their cages.
Here's the complete code:
public interface IZooAnimal
{
string Id { get; set; }
}
public interface ICage<out T> where T : IZooAnimal
{
IReadOnlyCollection<T> Animals { get; }
}
public class Cage<T> : ICage<T> where T: IZooAnimal
{
private readonly List<T> animals = new List<T>();
public IReadOnlyCollection<T> Animals
{
get
{
return animals.AsReadOnly();
}
}
public void CageAnimal(T animal)
{
animals.Add(animal);
}
}
public class Lion : IZooAnimal
{
public string Id { get; set; }
}
public class Zebra : IZooAnimal
{
public string Id { get; set; }
}
public class Zoo
{
public IList<ICage<IZooAnimal>> Cages { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var lion = new Lion();
var zebra = new Zebra();
var lionCage = new Cage<Lion>();
lionCage.CageAnimal(lion);
var zebraCage = new Cage<Zebra>();
zebraCage.CageAnimal(zebra);
var zoo = new Zoo();
zoo.Cages.Add(lionCage);
zoo.Cages.Add(zebraCage);
}
}
You should define your lists not with the concrete type that implements the interface, but with the interface:
var lionCage = new ZooCage<IZooAnimal>();
lionCage.Animals = new List<IZooAnimal>();
Then your code will work as expected.
The initial code did not work, because it is not allowed to convert concrete types to a generalised type (as #default.kramer pointed out covariance and contravariance).
The solution that i came up is following:
// your ZooCage is still generic
public class ZooCage<T>
{
// but you declare on creation which type you want to contain only!
private Type cageType = null;
public ZooCage(Type iMayContain)
{
cageType = iMayContain;
animals = new List<T>();
}
// check on add if the types are compatible
public void Add(T animal)
{
if (animal.GetType() != cageType)
{
throw new Exception("Sorry - no matching types! I may contain only " + cageType.ToString());
}
animals.Add(animal);
}
// should be generic but not visible to outher world!
private IList<T> animals { get; set; }
}
This code allows you to do:
var lion = new Lion();
var lionCage = new ZooCage<IZooAnimal>(typeof(Lion));
lionCage.Add(lion);
var zebra = new Zebra();
var zebraCage = new ZooCage<IZooAnimal>(typeof(Zebra));
zebraCage.Add(zebra);
But it will throw an error on:
zebraCage.Add(lion);
Now the zoo can be safely extended.