How to read with interfaces in the model? - c#

I would like to know how to read the JSON with a dynamic layout into my object that has an interface IPeople. When I try I get an error:
'Could not create an instance of type 'xxxx'. Type is an interface or abstract class and cannot be instantiated.
I have spent 2 days searching through Google and I tried using converters and it only works for one object at a time So I would have to choose between a Staff converter and a spectator converter.
How can I get both objects to load using the interface IPeople that loads to the Staff object if the staff data is detected and Spectator object if the spectator data is detected (possibly more object types all have unique properties).
{
"appCore":
{
"Crowd":
[
{
"Name": "Name1",
"Staff":
{
"PopupText":
[
{ "DisplayName":"No Popup Text","PopupValue":"", "IsSeperator":false},
{ "DisplayName":"--------------","PopupValue":"", "IsSeperator":true},
{ "DisplayName":"HT","PopupValue":"HT", "IsSeperator":false}
]
},
"Spectator":
{
"PopupText2":
[
{ "DisplayName":"No Popup Text","PopupValue":"", "Cheese":"hiih"},
{ "DisplayName":"--------------","PopupValue":"", "Cheese":"hiih"},
{ "DisplayName":"Half-Time","PopupValue":"Half-Time", "Cheese":"hiih"}
]
}
}
]
}
}
C# Models:
public class Crowd : ICrowd
{
public IPeople People { get; set; }
}
public class Staff : IPeople
{
IList<PopupTextData> PopupText { get; set; }
}
public class Spectator : IPeople
{
IList<PopupTextData2> PopupText2 { get; set; }
}
public class PopupTextData
{
public string DisplayName { get; set; }
public string PopupValue { get; set; }
public bool IsSeperator { get; set; }
}
public class PopupTextData2
{
public string DisplayName { get; set; }
public string PopupValue { get; set; }
public string Cheese { get; set; }
}
Code used to read the data:
settings.ForEach(type =>
{
builder.Register(c => c.Resolve<MCoreReader>().LoadSection(type))
.As(type.GetInterfaces())
.AsImplementedInterfaces()
.InstancePerRequest()
.InstancePerLifetimeScope();
});
public static object LoadSection(Type type, string _configurationFilePath, string _sectionNameSuffix)
{
if (!File.Exists(_configurationFilePath))
return Activator.CreateInstance(type);
var jsonFile = File.ReadAllText(_configurationFilePath);
var section = ToCamelCase(type.Name.Replace(_sectionNameSuffix, string.Empty));
var settingsData = JsonConvert.DeserializeObject<dynamic>(jsonFile, JsonSerializerSettings);
var settingsSection = settingsData[section];
return settingsSection == null
? Activator.CreateInstance(type)
: JsonConvert.DeserializeObject(JsonConvert.SerializeObject(settingsSection), type, JsonSerializerSettings);
}
private class SettingsReaderContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p =>
{
p.Writable = true;
p.Readable = true;
});
return props;
}
}
private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
ContractResolver = new SettingsReaderContractResolver(),
};

Assuming you control the Json, here's a working example: I've changed the class structure a bit.
It uses TypeNameHandling.All which writes the type information into the Json - as $type -, so when it's deserialized (using the same setting), it can restore the type.
void Main() {
var crowd = new Crowd {
People = new List<IPeople> {
new Staff {
PopupText = new List<PopupTextData> {
new PopupTextData { DisplayName = "Staff"}
}},
new Spectator {
PopupText = new List<PopupTextData> {
new PopupTextData { DisplayName = "Spectator"}
}},
}
};
var settings = new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
};
var json = JsonConvert.SerializeObject(crowd, settings);
var newCrowd = JsonConvert.DeserializeObject<ICrowd>(json, settings);
Console.WriteLine(newCrowd.People.Count); // outputs '2'
}
public interface ICrowd {
IList<IPeople> People { get; set; }
}
public interface IPeople {
IList<PopupTextData> PopupText { get; set; }
}
public class Crowd : ICrowd {
public IList<IPeople> People { get; set; }
}
public class Staff : IPeople {
public IList<PopupTextData> PopupText { get; set; }
}
public class Spectator : IPeople {
public IList<PopupTextData> PopupText { get; set; }
}
public class PopupTextData {
public string DisplayName { get; set; }
public string PopupValue { get; set; }
public bool IsSeperator { get; set; }
}

Related

How to store Multiple derived class in same list?

I have these classes
public class SubMenuItem : SubMenuVariant
{
public string SubMenuTitle { get; set; }
public LinkFieldType Link { get; set; }
public List<SubMenuSubItem> SubItems { get; set; }
}
public class SubMenuHighlightItem : SubMenuVariant
{
[JsonPropertyName(FieldNames.HighlightTitle)]
public string HighlightTitle { get; set; }
[JsonPropertyName(FieldNames.HighlightText)]
public string HighlightText { get; set; }
[JsonPropertyName(FieldNames.HighlightText)]
public Link HighLightLink { get; set; }
}
public class SubMenuVariant
{
}
Which I currently store in a List<SubMenuVariant> submenu
Problem is though I am not able to access the individual properties the different menues have, since they are being casted to a SubMenuVariant, which don't have any properties.
The list can only contain one type, at no point will both types exist in the list. The items is not being added explicitly to the list, but is being created by JsonSerializer.Deserialize a json request, which contains the properties, to the baseclass.
So the json can either look like this:
{
"submenu": [
{
"SubMenuTitle " : "Title",
"Link" : "Link",
"SubItems" : [
{...}
]
}
]
}
Or
{
"submenu": [
{
"HighlightTitle " : "Title",
"HighlightLink" : "Link",
"HighlightText" : "Text"
}
]
}
Is it somehow possible to store different class types in the same list?
Your issue is not that you can't store different types derived from the same base class. Your problem is accessing the members of the run-time types of the objects. That requires a cast. You can conditionally cast the items as you get them out of the list:
foreach (var smv in submenu)
{
var smi = smv as SubMenuItem;
if (smi != null)
{
// ...
}
else
{
var smhi = smv as SubMenuHighlightItem;
if (smhi != null)
{
// ...
}
}
}
In newer versions of C#, you can use pattern-matching:
foreach (var smv in submenu)
{
if (smv is SubMenuItem smi)
{
// ...
}
else if (smv is SubMenuHighlightItem smhi)
{
// ...
}
}
Here's an example of the pattern-matching option in action:
class Program
{
static void Main(string[] args)
{
var items = new List<BaseType>();
items.Add(new FirstDerivedType { FirstName = "One" });
items.Add(new SecondDerivedType { SecondName = "Two" });
items.Add(new FirstDerivedType { FirstName = "Three" });
items.Add(new SecondDerivedType { SecondName = "Four" });
foreach (var bt in items)
{
if (bt is FirstDerivedType fdt)
{
Console.WriteLine(fdt.FirstName);
}
else if (bt is SecondDerivedType sdt)
{
Console.WriteLine(sdt.SecondName);
}
}
}
}
public class FirstDerivedType : BaseType
{
public string FirstName { get; set; }
}
public class SecondDerivedType : BaseType
{
public string SecondName { get; set; }
}
public class BaseType
{
}
No, your solution is as good as it gets. The only other - worse - option being List<object>.
You can also try reflection, if you know the property name you can
access it as follows:
internal class Program
{
static void Main(string[] args)
{
List<SubMenuVariant> variants = new List<SubMenuVariant>();
variants.Add(new Sub1() { Title = "Test" });
variants.Add(new Sub2());
var prop = variants.First().GetType().GetProperty("Title");
prop?.GetValue(variants.First(), null);
}
}
public class Sub1 :SubMenuVariant
{
public string Title { get; set; }
}
public class Sub2: SubMenuVariant
{
public int Index { get; set; }
}
public class SubMenuVariant
{
}
This will generate the following result:

AutoMapper to map a child list object

I'm using an generic method to map two classes using Automapper
My generic methods
public class AutoMapperConfiguration
{
public MapperConfiguration Configure<TSource, TDestination>() where TSource:class where TDestination:class
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<ClientMappingProfile<TSource,TDestination>>();
});
return config;
}
}
ClientMappingProfile.cs
public class ClientMappingProfile<TSource,TDestination>: Profile where TSource : class where TDestination:class
{
public ClientMappingProfile()
{
CreateMap<TSource, TDestination>().ReverseMap();
}
}
StudentDetailsViewModel.cs
public class StudentDetailsViewModel
{
public long ID { get; set; }
public string FirstName { get; set; }
public List<QualificationViewModel> listQualificationViewModel { get; set; }
}
QualificationViewModel.cs
public class QualificationViewModel
{
public long ID { get; set; }
public long StudentID { get; set; }
public string ExaminationPassed { get; set; }
}
StudentValueObject.cs
public class StudentValueObject
{
public long ID { get; set; }
public string FirstName { get; set; }
public List<StudentQualificationValueObject> listStudentQualificationValueObject { get; set; }
}
StudentQualificationValueObject.cs
public class StudentQualificationValueObject
{
public long ID { get; set; }
public long StudentID { get; set; }
public string ExaminationPassed { get; set; }
}
Usage
StudentValueObject studentValueObject = new StudentValueObject();
var config = new AutoMapperConfiguration().Configure<StudentValueObject, StudentDetailsViewModel>();
var iMapper = config.CreateMapper();
studentValueObject = iMapper.Map<StudentDetailsViewModel, StudentValueObject>(objStudentModel);
So, this works fine with Mapping StudentDetailsViewModel.cs with StudentValueObject.cs. But it silently fails to copy my child list objects which is List<QualificationViewModel> to List<StudentQualificationValueObject>. The child list object always seems to be null. I'm pretty newbie to AutoMapper. I need some help as to know where am I going wrong or what need to be added/fixed to my generic method, so that the child list object gets copied to with Parent object.
Update -
Currently I'm doing it using below code and its working properly but I'm confused is this the proper way of doing this.
StudentValueObject studentValueObject = new StudentValueObject();
var config = new AutoMapperConfiguration().Configure<StudentValueObject, StudentDetailsViewModel>();
var iMapper = config.CreateMapper();
studentValueObject = iMapper.Map<StudentDetailsViewModel, StudentValueObject>(objStudentModel);
config = new AutoMapperConfiguration().Configure<StudentQualificationValueObject, QualificationViewModel>();
iMapper = config.CreateMapper();
studentValueObject.listStudentQualificationValueObject = iMapper.Map<List<QualificationViewModel>, List<StudentQualificationValueObject>>(objStudentModel.listQualificationViewModel);
You have to map the list properties, cause they have different names in the given parent types and you have to add a mapping for the types used within both lists. Here is an working example for your code:
public class StudentsMappingProfile : Profile
{
public StudentsMappingProfile()
{
CreateMap<StudentValueObject, StudentDetailsViewModel>()
.ForMember(viewModel => viewModel.listQualificationViewModel, conf => conf.MapFrom(value => value.listStudentQualificationValueObject));
CreateMap<StudentQualificationValueObject, QualificationViewModel>();
}
}
public class Program
{
public static void Main()
{
var config = new MapperConfiguration(cfg => cfg.AddProfile<StudentsMappingProfile>());
var mapper = config.CreateMapper();
var source = new StudentValueObject { ID = 73, FirstName = "Hello", listStudentQualificationValueObject = new List<StudentQualificationValueObject> { new StudentQualificationValueObject { ID = 42, StudentID = 17, ExaminationPassed = "World" } } };
var destination = mapper.Map<StudentDetailsViewModel>(source);
Console.ReadKey();
}
}

C# Accessing custom attribute of owner object

How can I access the custom attribute of the parent or owner object.
Look at the FieldInfo property of the SQLFieldInfo struct
Here's a more detailed program that will compile and run that shows what I need.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Employee myclass = new Employee();
// Load from sql server...
myclass.Name = "Alain";
myclass.Age = 51;
//----
MessageBox.Show(myclass.Name.ToString()); // Should return Alain
MessageBox.Show(myclass.Age.FieldInfo.Type.ToString()); // Should output "int"
}
}
// This next class is generated by a helper exe that reads SQL table design and create the class from it
[SQLTableAttribute(DatabaseName = "Employees", Schema = "dbo", TableName = "Employees")]
public class Employee
{
[SQLFieldAttribute(FieldName = "ID", Type = SqlDbType.Int)]
public SQLFieldInfo<int> ID { get; set; }
[SQLFieldAttribute(FieldName = "Name", Type = SqlDbType.NVarChar, Size = 200)]
public SQLFieldInfo<String> Name { get; set; }
[SQLFieldAttribute(FieldName = "Age", Type = SqlDbType.Int)]
public SQLFieldInfo<int> Age { get; set; }
}
public struct SQLFieldInfo<T>
{
private readonly T value;
public SQLFieldInfo(T Value)
{
this.value = Value;
}
public static implicit operator SQLFieldInfo<T>(T Value)
{
return new SQLFieldInfo<T>(Value);
}
public T Value
{
get
{
return this.value;
}
}
public override string ToString()
{
return this.value.ToString();
}
public SQLFieldAttribute FieldInfo
{
get
{
// Need to retreive the attribute class of the parent or declaring member
return null;
}
}
}
// Holds the sql field information
public class SQLFieldAttribute : Attribute
{
public string FieldName { get; set; }
public SqlDbType Type { get; set; }
public bool AllowNull { get; set; }
public int Size { get; set; }
}
// Holds the sql table information
public class SQLTableAttribute : Attribute
{
public string DatabaseName { get; set; }
public string Schema { get; set; } = "dbo";
public string TableName { get; set; }
}
Thank you!
Alain
My data class is as follows (should be fairly translatable to A above):
public class Foo
{
[Argument(Help = "Name", AssignmentDelimiter = "=")]
public string Name
{
get;
set;
}
}
A helper class is responsible of reading attribute values of objects:
static public string GetCommandLineDelimiter<T>(Expression<Func<T>> property)
{
if(property != null)
{
var memberExpression = (MemberExpression)property.Body;
string propertyName = memberExpression.Member.Name;
PropertyInfo prop = typeof(Arguments).GetProperty(propertyName);
if(prop != null)
{
object[] dbFieldAtts = prop.GetCustomAttributes(typeof(ArgumentAttribute), true);
if(dbFieldAtts.Length > 0)
{
return ((ArgumentAttribute)dbFieldAtts[0]).AssignmentDelimiter;
}
}
}
return null;
}
To use it, simply:
string delimiter = GetCommandLineDelimiter(() => myObject.Name);
That will get the attribute value of AssignmentDelimiter on property Name, i.e. "=".
First, MSDN is your friend.
Then, if you want to get the attributes for ancestors just specify true in the inherit flag of the method:
var attribute = typeof(A).GetProperty("myprop").GetCustomAttributes(true)
.OfType<MycustomAttrib>().FirstOrDefault();
This works. I am doing a lazy initialization of a reference to the custom attribute by using reflection to look at all the properties of all the types.
public class MycustomAttribAttribute : Attribute
{
public MycustomAttribAttribute(string name)
{
this.Name=name;
}
public string Name { get; private set; }
}
class A
{
public A() { MyProp=new B(); }
[MycustomAttrib(name: "OK")]
public B MyProp { get; set; }
}
class B
{
private static Lazy<MycustomAttribAttribute> att = new Lazy<MycustomAttribAttribute>(() =>
{
var types = System.Reflection.Assembly.GetExecutingAssembly().DefinedTypes;
foreach(var item in types)
{
foreach(var prop in item.DeclaredProperties)
{
var attr = prop.GetCustomAttributes(typeof(MycustomAttribAttribute), false);
if(attr.Length>0)
{
return attr[0] as MycustomAttribAttribute;
}
}
}
return null;
});
public string MyProp2
{
get
{
return att.Value.Name;
}
}
}
class Program
{
static void Main(string[] args)
{
// Finds the attribute reference and returns "OK"
string name = (new A()).MyProp.MyProp2;
// Uses the stored attribute reference to return "OK"
string name2 = (new A()).MyProp.MyProp2;
}
}

Deserialize Dictionary<string, T>

For example, I have a some classes:
class User
{
int Id {get; set;}
string Name {get; set;}
}
class Venue
{
int Id {get; set;}
string Adress {get; set;}
}
class Message
{
string Text {get; set;}
int FromId {get; set;}
}
I take the json from web:
[%user% => {id: 1, name: "Alex"}, %user% => {id: 5, name: "John"}]
I can parse it :
var myObjects = JsonConvert.DeserializeObject<Dictionary<string, User>>(json);
But if have a json:
[%user% => {id: 1, name: "Alex"}, %venue% => {id: 465, adress: "Thomas at 68th Street"}, %message% => {text: "hello", fromId: 78}]
I can define type by key %user% = User, %venue% = Venue, etc..
But how can I parse it?
Thanks in advance!
UPDATE
My current solution:
private JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Full
};
string myJson = "{\"%user%\":{\"id\" : 5, \"name\" : \"John\"}, \"%venue%\":{\"id\" : \"5f56de\", \"adress\": \"Thomas at 68th Street\"}}";
Dictionary<string, object> dict =
JsonConvert.DeserializeObject<Dictionary<string, object>>
(myJson, _jsonSettings);
Dictionary<string, object> d = new Dictionary<string, object>();
foreach(var o in dict)
{
string json = (string)o.Value.ToString();
switch (o.Key)
{
case "%user%":
{
var v = JsonConvert.DeserializeObject<User>(json);
d.Add(o.Key, v);
break;
}
case "%venue%":
{
var v = JsonConvert.DeserializeObject<Venue>(json);
d.Add(o.Key, v);
break;
}
case "%message%":
{
var v = JsonConvert.DeserializeObject<Message>(json);
d.Add(o.Key, v);
break;
}
}
}
If you are using Json.Net (aka Newtonsoft.Json) you can create a custom JsonConverter object. This object would allows for custom parsing of the json. So, given the following classes
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Venue
{
public string Id { get; set; }
public string Address { get; set; }
}
public class Message
{
public string Text { get; set; }
[JsonProperty("fromId")]
public string FromId { get; set; }
}
You could contain those within another class that is assigned a JsonConverter
[JsonConverter(typeof(PostJsonConverter))]
public class Post
{
public User User { get; set; }
public Venue Venue { get; set; }
public Message Message { get; set; }
}
The JsonConvter class is an abstract class with three methods you need to overwrite. You'll want to implement the ReadJson method. If you do not need to write json, then no need to do anything in the WriteJson method.
public class PostJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// not implemented
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// it must be an object being passed in, if not something went wrong!
if (reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();
var postToken = JToken.ReadFrom(reader);
var userToken = postToken["%user%"];
var venueToken = postToken["%venue%"];
var messageToken = postToken["%message%"];
return new Post
{
User = userToken == null ? null : userToken.ToObject<User>(),
Venue = venueToken == null ? null : venueToken.ToObject<Venue>(),
Message = messageToken == null ? null : messageToken.ToObject<Message>(),
};
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
No extra work is needed to convert this from normal conversion because we've given the class the JsonConverterAttribute.
string myJson = "{\"%user%\":{\"id\" : 5, \"name\" : \"John\"}, \"%venue%\":{\"id\" : \"5f56de\", \"address\": \"Thomas at 68th Street\"}}";
Post post = JsonConvert.DeserializeObject<Post>(myJson);
Here's a concise possible solution, using my small JSON parser library:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Venue
{
public int Id { get; set; }
public string Address { get; set; }
}
public class Message
{
public string Text { get; set; }
public int FromId { get; set; }
}
/* Deals with this SO question :
*
* http://stackoverflow.com/questions/19023696/deserialize-dictionarystring-t
*/
public static void SO_19023696()
{
Console.Clear();
Console.WriteLine("StackOverflow question 19023696 - Polymorphic, key-driven Test");
Console.WriteLine();
string myJson = #"
[
{
""%user%"" : { ""id"": 1, ""name"": ""Alex""} ,
""%venue%"" : { ""id"": 465, ""address"": ""Thomas at 68th Street"" },
""%message%"" : { ""text"": ""hello"", ""fromId"": 78 }
},
{
""%user%"" : { ""id"": 2, ""name"": ""Carl""} ,
""%message%"" : { ""text"": ""bye"", ""fromId"": 79 }
}
]";
Dictionary<string, object>[] parsed =
JSON.Map(null as Dictionary<string, object>[]).
FromJson
(
myJson,
JSON.Map(default(Dictionary<string, object>)).
Using // Deal with the main issue raised by the SO question:
(
(outer, type, value) =>
((outer.Hash != null) && outer.Hash.ContainsKey("Name") ? (Func<object>)
(() => new User { Id = (int)outer.Hash["Id"], Name = (string)outer.Hash["Name"] }) :
((outer.Hash != null) && outer.Hash.ContainsKey("Address") ? (Func<object>)
(() => new Venue { Id = (int)outer.Hash["Id"], Address = (string)outer.Hash["Address"] }) :
((outer.Hash != null) && outer.Hash.ContainsKey("Text") ? (Func<object>)
(() => new Message { FromId = (int)outer.Hash["FromId"], Text = (string)outer.Hash["Text"] }) :
null
)
)
)
),
Sample_Revivers.CamelCaseToPascalCase,
Sample_Revivers.DoubleToInteger
);
System.Diagnostics.Debug.Assert(parsed[0]["%user%"] is User);
System.Diagnostics.Debug.Assert(parsed[0]["%venue%"] is Venue);
System.Diagnostics.Debug.Assert(parsed[0]["%message%"] is Message);
System.Diagnostics.Debug.Assert(parsed[1]["%user%"] is User);
System.Diagnostics.Debug.Assert(parsed[1]["%message%"] is Message);
Console.Write("Passed - Press a key...");
Console.ReadKey();
}
More use cases/examples, via generic dictionaries, or anonymous types, or POCOs, can be found here:
https://code.google.com/p/ysharp/source/browse/trunk/TestJSONParser/BasicTests.cs

Protobuf Inheritance and Generics

I am attempting to use ProtoBuf net to serialize an object tree with the classes in the following format:
[ProtoContract]
class MySpecialCollectionList<T> : List<MySpecialCollection<T>>
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class MySpecialCollection<T> : List<Special<T>>
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Special<T>
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public string Description { get; set; }
[ProtoMember(3)]
private readonly T _source;
T Source { get { return _source; } }
private Special()
{
}
public Special(T source)
{
_source = source;
}
}
interface IBeast
{
string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
using (var fs = File.Create(#"c:\temp\protobuftest.bin"))
{
Serializer.Serialize(fs, collectionList);
fs.Close();
}
}
private MySpecialCollectionList<IBeast> GetSpecialCollectionList()
{
var ant = new Ant() { Name = "Mr Ant" };
var cat = new Cat() { Name = "Mr Cat" };
var dog = new Dog() { Name = "Mr Dog" };
var Special = new Special<IBeast>(ant);
var specialCollection1 = new MySpecialCollection<IBeast>() {
{new Special<IBeast>(ant)},
{new Special<IBeast>(cat)},
{new Special<IBeast>(dog)}
};
specialCollection1.Name = "Special Collection1";
var specialCollection2 = new MySpecialCollection<IBeast>() {
{new Special<IBeast>(ant)},
{new Special<IBeast>(dog)}
};
specialCollection2.Name = "Special Collection2";
var specialCollectionList = new MySpecialCollectionList<IBeast>() {
specialCollection1, specialCollection2 };
specialCollectionList.Name = "Special Collection List";
return specialCollectionList;
}
}
Notice how the class I am serializing (MySpecialCollectionList<T>) is derived from a List<SomeOtherClass<T>>, not just List<T>.
I am struggling to work out where to put "ProtoInclude" attributes to get this to serialize all the items in the MySpecialCollectionList. Any help would be much appreciated.
Inheritance is not an issue here since even if A : B it is not true that Foo<A> : Foo<B>. Note that protobuf-net won't use a non-default constructor, although it is possible to skip the constructor, binding to the field directly (even readonly). While you may have 6 T, I can't see (from the code) that it would ever be in doubt which closed type you intend, and if the closed type is known you should be set.
If you have a Foo<SomeBaseClass> and a number of concrete types inherited from SomeBaseClass then the markers would o on SomeBaseClass.
However, if you have a concrete scenario I can use to reproduce your issue, I'll happily take a look.
Updated re edit:
There are a couple of key points drawn out in the example:
in common with most binding APIs, XmlSerializer and IIRC DataContractSerializer, an item is either a list xor an item with values; if a collection (something implementing IList) has properties itself, they will not be serialized; encapsulation is preferred over inheritance here, i.e. something that has a Name and has a list (rather than has a Name and is a list)
protobuf-net v1 does not support interface-based serialization; v2 does, but as with XmlSerializer and DataContractSerializer you need to explicitly tell it what things it needs to expect; quite nicely, though, we can move the [ProtoMember] onto the interface itself
Here's a fully working version in v2:
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
class MySpecialCollectionList<T>
{
[ProtoMember(1)]
public string Name { get; set; }
private readonly List<MySpecialCollection<T>> items = new List<MySpecialCollection<T>>();
[ProtoMember(2)]
public List<MySpecialCollection<T>> Items { get { return items; } }
}
[ProtoContract]
class MySpecialCollection<T>
{
[ProtoMember(1)]
public string Name { get; set; }
private readonly List<Special<T>> items = new List<Special<T>>();
[ProtoMember(2)]
public List<Special<T>> Items { get { return items; } }
}
[ProtoContract]
class Special<T>
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public string Description { get; set; }
[ProtoMember(3)]
private readonly T _source;
T Source { get { return _source; } }
private Special()
{
}
public Special(T source)
{
_source = source;
}
}
[ProtoContract]
[ProtoInclude(2, typeof(Ant))]
[ProtoInclude(3, typeof(Cat))]
[ProtoInclude(4, typeof(Dog))]
interface IBeast
{
[ProtoMember(1)]
string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
public string Name { get; set; }
}
public static class Form1
{
private static void Main()
{
MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
var copy = Serializer.DeepClone(collectionList);
}
private static MySpecialCollectionList<IBeast> GetSpecialCollectionList()
{
var ant = new Ant() { Name = "Mr Ant" };
var cat = new Cat() { Name = "Mr Cat" };
var dog = new Dog() { Name = "Mr Dog" };
var Special = new Special<IBeast>(ant);
var specialCollection1 = new MySpecialCollection<IBeast>() {Items =
{new Special<IBeast>(ant),
new Special<IBeast>(cat),
new Special<IBeast>(dog)}
};
specialCollection1.Name = "Special Collection1";
var specialCollection2 = new MySpecialCollection<IBeast>()
{
Items =
{new Special<IBeast>(ant),
new Special<IBeast>(dog)}
};
specialCollection2.Name = "Special Collection2";
var specialCollectionList = new MySpecialCollectionList<IBeast>()
{
Items ={
specialCollection1, specialCollection2 }
};
specialCollectionList.Name = "Special Collection List";
return specialCollectionList;
}
}

Categories