Let's assume the following minimal setup
using MessagePack;
using MyClasses;
var myList = new List<Class1>();
myList.Add(new Class3() { SomeProp = "test" });
var ser = MessagePackSerializer.Serialize(myList);
var deser = MessagePackSerializer.Deserialize<List<Class1>>(ser);
MyClasses are:
namespace MyClasses
{
[MessagePackObject(keyAsPropertyName: true)]
[MessagePack.Union(0, typeof(Class2))]
public abstract class Class1
{
}
[MessagePackObject(keyAsPropertyName: true)]
[MessagePack.Union(1, typeof(Class3))]
public abstract class Class2 : Class1
{
}
[MessagePackObject(keyAsPropertyName: true)]
public class Class3 : Class2
{
public string SomeProp { get; set; }
}
}
After serializing and deserializing, the variable deser (type List) consists of one element which is null.
How should MessagePack be applied here?
Related
I have serialized a complex object( containing abstract classes, read only properties) using Newtonsoft.Jsonconverter SerializeObject successfully.
While trying to Deserialize the same using DeserializeObject method, it throws following error
- An item with this key has already been added.
On further investigation I found out that there might be some properties in the object with same name. But I couldn't find any property name being repeated in json file being de-serialized.
Version of NewtonSoft Json : 8.0.3
Hi I was trying to replicate your error but actually what I got was a successful result. this is the test I did :
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var child = new Child();
var serializesObject = JsonConvert.SerializeObject(child);
var deserializedObject = JsonConvert.DeserializeObject(serializesObject, typeof(Child));
}
}
public abstract class Abstract
{
public int Prop1 { get; set; }
public readonly string Prop2;
public List<string> Prop3 { get; set; }
public int[] Prop4 { get; set; }
public abstract void Hey();
public Abstract()
{
Prop1 = 1;
Prop2 = "2";
Prop3 = new List<string>();
Prop4 = new int[4];
}
}
public class Child : Abstract
{
public readonly string Prop5;
public Child()
{
Prop5 = "5";
}
public override void Hey()
{
throw new NotImplementedException();
}
}
}
I hope that this code can help you to get to the expected result you want.
Cheers,
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 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
How do I get properties from derived class in base class?
Base class:
public abstract class BaseModel {
protected static readonly Dictionary<string, Func<BaseModel, object>>
_propertyGetters = typeof(BaseModel).GetProperties().Where(p => _getValidations(p).Length != 0).ToDictionary(p => p.Name, p => _getValueGetter(p));
}
Derived classes:
public class ServerItem : BaseModel, IDataErrorInfo {
[Required(ErrorMessage = "Field name is required.")]
public string Name { get; set; }
}
public class OtherServerItem : BaseModel, IDataErrorInfo {
[Required(ErrorMessage = "Field name is required.")]
public string OtherName { get; set; }
[Required(ErrorMessage = "Field SomethingThatIsOnlyHereis required.")]
public string SomethingThatIsOnlyHere{ get; set; }
}
In this example - can I get the "Name" property from ServerItem class while in BaseModel class?
EDIT:
I'm trying to implement model validation, as described here:
http://weblogs.asp.net/marianor/archive/2009/04/17/wpf-validation-with-attributes-and-idataerrorinfo-interface-in-mvvm.aspx
I figured that if I create some base model with (almost) all of the validation magic in it, and then extend that model, it will be okay...
If both classes are in the same assembly, you can try this:
Assembly
.GetAssembly(typeof(BaseClass))
.GetTypes()
.Where(t => t.IsSubclassOf(typeof(BaseClass))
.SelectMany(t => t.GetProperties());
This will give you all the properties of all the subclasses of BaseClass.
If you require that a derived class must implement a method or property, you should introduce that method or property into the base class as an abstract declaration.
For example, for your Name property, you would add to the base class:
public abstract string Name { get; set; }
Then any derived classes must implement it, or be abstract classes themselves.
Once you have added the abstract version of the Name property to the base class, you will be able to access it in the base class anywhere except in the base class's constructor.
If you must do literally fetch property of derived class from within base class, you can use Reflection, for example - like this...
using System;
public class BaseModel
{
public string getName()
{
return (string) this.GetType().GetProperty("Name").GetValue(this, null);
}
}
public class SubModel : BaseModel
{
public string Name { get; set; }
}
namespace Test
{
class Program
{
static void Main(string[] args)
{
SubModel b = new SubModel();
b.Name = "hello";
System.Console.Out.WriteLine(b.getName()); //prints hello
}
}
}
This is not recommended, though, and you most probably should rethink your design like Matthew said.
As for not throwing properties to your base classes -- you can try to decouple base and deriving classes into unrelated objects and pass them via constructors.
Another way to solve this issue by create virtual property in base class and override it to derived class.
public class Employee
{
public virtual string Name {get; set;}
}
public class GeneralStaff
{
public override string Name {get; set;}
}
class Program
{
static void Main(string[] args)
{
Employee emp = new GeneralStaff();
emp.Name = "Abc Xyz";
//---- More code follows----
}
}
Okay, I solved this problem slightly different than the author of this post: http://weblogs.asp.net/marianor/archive/2009/04/17/wpf-validation-with-attributes-and-idataerrorinfo-interface-in-mvvm.aspx
public abstract class BaseModel : IDataErrorInfo {
protected Type _type;
protected readonly Dictionary<string, ValidationAttribute[]> _validators;
protected readonly Dictionary<string, PropertyInfo> _properties;
public BaseModel() {
_type = this.GetType();
_properties = _type.GetProperties().ToDictionary(p => p.Name, p => p);
_validators = _properties.Where(p => _getValidations(p.Value).Length != 0).ToDictionary(p => p.Value.Name, p => _getValidations(p.Value));
}
protected ValidationAttribute[] _getValidations(PropertyInfo property) {
return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true);
}
public string this[string columnName] {
get {
if (_properties.ContainsKey(columnName)) {
var value = _properties[columnName].GetValue(this, null);
var errors = _validators[columnName].Where(v => !v.IsValid(value)).Select(v => v.ErrorMessage).ToArray();
return string.Join(Environment.NewLine, errors);
}
return string.Empty;
}
}
public string Error {
get { throw new NotImplementedException(); }
}
}
Maybe it will help somebody.
Scan your assembly for all inherited classes from BaseModel and create dictionary like this:
Dictionary<Type, Dictionary<string, Func<BaseModel, object>>>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TESTNEW
{
public abstract class BusinessStructure
{
public BusinessStructure()
{ }
public string Name { get; set; }
public string[] PropertyNames{
get
{
System.Reflection.PropertyInfo[] Pr;
System.Type _type = this.GetType();
Pr = _type.GetProperties();
string[] ReturnValue = new string[Pr.Length];
for (int a = 0; a <= Pr.Length - 1; a++)
{
ReturnValue[a] = Pr[a].Name;
}
return ReturnValue;
}
}
}
public class MyCLS : BusinessStructure
{
public MyCLS() { }
public int ID { get; set; }
public string Value { get; set; }
}
public class Test
{
void Test()
{
MyCLS Cls = new MyCLS();
string[] s = Cls.PropertyNames;
for (int a = 0; a <= s.Length - 1; a++)
{
System.Windows.Forms.MessageBox.Show(s[a].ToString());
}
}
}
}
I want to project info from a list of base-class instances into a list of derived-class instances, but I keep running into casting exceptions. Here's an example of what I'm trying to do... how do I make it work?
The following code is up at http://ideone.com/CaXQS , if that helps... thanks in advance!
using System;
using System.Collections.Generic;
namespace AddingReversedNumbers
{
public class MyDerivedClass : MyBaseClass, IMyInterface
{
public int InterfaceProperty { get; set; }
public int DerivedClassProperty { get; set; }
public List<int> DerivedClassList { get; set; }
}
public class MyBaseClass
{
public int BaseClassProperty { get; set; }
}
public interface IMyInterface
{
int InterfaceProperty { get; set; }
}
class Program
{
static void Main()
{
//// This code works just fine.
//var derivedList = new List<MyDerivedClass>();
//derivedList.Add(new MyDerivedClass { BaseClassProperty = 10, DerivedClassProperty = 20, InterfaceProperty = 30 });
//derivedList.Add(new MyDerivedClass { BaseClassProperty = 20, DerivedClassProperty = 40, InterfaceProperty = 60 });
//var baseList = derivedList.ConvertAll(x => (MyBaseClass)x);
// This code breaks when ConvertAll() is called.
var baseList = new List<MyBaseClass>();
baseList.Add(new MyBaseClass{ BaseClassProperty = 10 });
baseList.Add(new MyBaseClass{ BaseClassProperty = 20 });
var derivedList = baseList.ConvertAll(x => (MyDerivedClass)x);
}
}
}
In your ConvertAll code you're just casting. You can't cast a base class instance to a derived class instance. For example, what would you expect casting object to FileStream to do? Which file would it refer to?
If you want to create a new object in the ConvertAll projection, just do that:
var derivedList = baseList.ConvertAll
(x => new MyDerivedClass { BaseClassProperty = x.BaseClassProperty });