If i have objects with properties of type object or objects that are generics, how can i serialize this?
Eg.
public class MyClass
{
public Object Item { get; set; }
}
or
public class MyClass<T>
{
public T Item { get; set; }
}
EDIT
My Generic class now looks like this:
public class MyClass<T>
{
public MySubClass<T> SubClass { get; set; }
}
public class MySubClass<T>
{
public T Item { get; set; }
}
Additonal question: How can i change the element name for Item at runtime to typeof(T).Name?
Have you tried the [Serializable] attribute?
[Serializable]
public class MySerializableClass
{
public object Item { get; set; }
}
[Serializable]
public class MySerializableGenericClass<T>
{
public T Item { get; set; }
}
Although the generic class is only serializable if the generic type parameter is serializable as well.
Afaik there is no way to constrain the type parameter to be serializable. But you can check at runtime using a static constructor:
[Serializable]
public class MySerializableGenericClass<T>
{
public T Item { get; set; }
static MySerializableGenericClass()
{
ConstrainType(typeof(T));
}
static void ConstrainType(Type type)
{
if(!type.IsSerializable)
throw new InvalidOperationException("Provided type is not serializable");
}
}
Use these methods to serialize\deserialize any object (even generics) to an XML file, though can be modified to suit other purposes:
public static bool SerializeTo<T>(T obj, string path)
{
XmlSerializer xs = new XmlSerializer(obj.GetType());
using (TextWriter writer = new StreamWriter(path, false))
{
xs.Serialize(writer, obj);
}
return true;
}
public static T DeserializeFrom<T>(string path)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
using (TextReader reader = new System.IO.StreamReader(path))
{
return (T)xs.Deserialize(reader);
}
}
Related
I have a method which takes an object as a parameter. Within that method I walk through that objects properties with reflection. Some properties are of a generic class type. I like to read a property of that generic class property, but I cannot cast it to a generic class.
public abstract class BaseClass
{
public int Id { get; set; }
}
public abstract class GenericClass<T>: BaseClass
{
public string Description { get; set; }
}
public class DerivedClassA: GenericClass<DerivedClassA>
{
public string A { get; set; }
}
public class DerivedClassB: GenericClass<DerivedClassB>
{
public string B { get; set; }
}
public class ReflectingClass: BaseClass
{
public string Code { get; set; }
public DerivedClassA DerA { get; set; }
public DerivedClassB DerB { get; set; }
}
public static void Reflecting(object obj)
{
var t = GetType(obj)
foreach (var pi in t.GetProperties())
{
if (obj.GetType().BaseType.GetGenericTypeDefinition() == typeof(GenericClass<>)
{
var genClassObjProperty = ((GenericClass<T>)obj).Description; // Error, cannot do this at all !!!
}
}
}
What I want is for the code to walk to the properties and whatever the derived class actually is get the Description property of the GenericClass it is derived from.
I am using a generic class, because elsewhere in the code I call methods by their derived class and get the proper class type without resorting to all kinds of cast and passing types. I.e:
DerivedClassA.DoSomething()
instead of
BaseClass.DoSomething<DerivedClassA>()
or
BaseClass.DoSomething(type derivedClassType)
Take a look at this:
public static void Reflecting(object obj)
{
foreach (var pi in obj.GetType().GetProperties())
{
if (pi.PropertyType.BaseType.IsGenericType
&& pi.PropertyType.BaseType.GetGenericTypeDefinition()
== typeof(GenericClass<>))
{
var propValue = pi.GetValue(obj);
if (propValue != null)
{
var description = propValue.GetType()
.GetProperty("Description").GetValue(propValue);
Console.WriteLine(description);
}
}
}
Console.ReadKey();
}
I think this is what you need.
I store the items displayed on my homepage as an XML string (in the settings).
<?xml version=""1.0""?>
<HomePageItemList>
<PlantHomePageItem>
<Name>Plant1</Name>
</PlantHomePageItem>
<PlantHomePageItem>
<Name>Plant2</Name>
</PlantHomePageItem>
<AdminHomePageItem>
<Name>Admin1</Name>
</AdminHomePageItem>
</HomePageItemList>
Some items represent a plant PlantHomePageItem, others an admin tool AdminHomePageItem. Both inherit the same base class HomePageItem
[XmlRoot("HomePageItemList")]
public class TestSerialization
{
[XmlArray("HomePageItemList")]
[XmlArrayItem("PlantHomePageItem", Type = typeof(PlantHomePageItem))]
[XmlArrayItem("AdminHomePageItem", Type = typeof(AdminHomePageItem))]
public List<HomePageItem> HomePageItemList { get; set; }
}
[XmlInclude(typeof(PlantHomePageItem))]
[XmlInclude(typeof(AdminHomePageItem))]
public class HomePageItem
{
[XmlElement("Name")]
public string Name { get; set; }
}
public class PlantHomePageItem : HomePageItem { }
public class AdminHomePageItem : HomePageItem { }
When I try an deserialize it, I have no runtime error, just an empty object...
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(XML));
XmlSerializer xs = new XmlSerializer(typeof(TestSerialization));
TestSerialization obj = (TestSerialization)xs.Deserialize(ms);
Console.WriteLine(obj.HomePageItemList.Count);
foreach (var item in obj.HomePageItemList)
{
Console.WriteLine("{0}: {1}", item.GetType().Name, item.Name);
}
returns
0
A .NET Fiddle with the entire thing is available: https://dotnetfiddle.net/f51b0K
Change your TestSerialization class to this...
[XmlRoot("HomePageItemList")]
public class TestSerialization
{
[XmlElement("PlantHomePageItem", Type = typeof(PlantHomePageItem))]
[XmlElement("AdminHomePageItem", Type = typeof(AdminHomePageItem))]
public List<HomePageItem> HomePageItemList { get; set; }
}
Your modified example...
Using YamlDotNet, I am attempting to deserialize the following YAML:
Collection:
- Type: TypeA
TypeAProperty: value1
- Type: TypeB
TypeBProperty: value2
The Type property is a required property for all objects under Collection. The rest of the properties are dependent on the type.
This is my ideal object model:
public class Document
{
public IEnumerable<IBaseObject> Collection { get; set; }
}
public interface IBaseObject
{
public string Type { get; }
}
public class TypeAClass : IBaseObject
{
public string Type { get; set; }
public string TypeAProperty { get; set; }
}
public class TypeBClass : IBaseObject
{
public string Type { get; set; }
public string TypeBProperty { get; set; }
}
Based on my reading, I think my best bet is to use a custom node deserializer, derived from INodeDeserializer. As a proof of concept, I can do this:
public class MyDeserializer : INodeDeserializer
{
public bool Deserialize(IParser parser, Type expectedType, Func<IParser, Type, object> nestedObjectDeserializer, out object value)
{
if (expectedType == typeof(IBaseObject))
{
Type type = typeof(TypeAClass);
value = nestedObjectDeserializer(parser, type);
return true;
}
value = null;
return false;
}
}
My issue now is how to dynamically determine the Type to choose before calling nestedObjectDeserializer.
When using JSON.Net, I was able to use a CustomCreationConverter, read the sub-JSON into a JObject, determine my type, then create a new JsonReader from the JObject and re-parse the object.
Is there a way I can read, roll-back, then re-read nestedObjectDeserializer?
Is there another object type I can call on nestedObjectDeserializer, then from that read the Type property, finally proceed through normal YamlDotNet parsing of the derived type?
It's not easy. Here is an GitHub issue explaining how to do polymorphic serialization using YamlDotNet.
A simple solution in your case is to to 2-step deserialization. First you deserialize into some intermediary form and than convert it to your models. That's relatively easy as you limit digging in the internals of YamlDotNet:
public class Step1Document
{
public List<Step1Element> Collection { get; set; }
public Document Upcast()
{
return new Document
{
Collection = Collection.Select(m => m.Upcast()).ToList()
};
}
}
public class Step1Element
{
// Fields from TypeA and TypeB
public string Type { get; set; }
public string TypeAProperty { get; set; }
public string TypeBProperty { get; set; }
internal IBaseObject Upcast()
{
if(Type == "TypeA")
{
return new TypeAClass
{
Type = Type,
TypeAProperty = TypeAProperty
};
}
if (Type == "TypeB")
{
return new TypeBClass
{
Type = Type,
TypeBProperty = TypeBProperty
};
}
throw new NotImplementedException(Type);
}
}
And that to deserialize:
var serializer = new DeserializerBuilder().Build();
var document = serializer.Deserialize<Step1Document>(data).Upcast();
I tried to create a method in a ApiController that looks like this:
public DemoList<Demo> GetAll()
{
var result = new DemoList<Demo>() { new Demo(){Y=2}, new Demo(), new Demo(){Y=1} };
result.Name = "Test";
return result;
}
Demo and DemoList look like this:
public interface INamedEnumerable<out T> : IEnumerable<T>
{
string Name { get; set; }
}
public class Demo
{
public int X { get { return 3; } }
public int Y { get; set; }
}
public class DemoList<T> : List<T>, INamedEnumerable<T>
{
public DemoList()
{
}
public string Name { get; set; }
}
I then cheked the ouput with fiddler
GET http://localhost:8086/api/Demo
and got the following:
XML (Accept header set to application/xml)
<ArrayOfDemo xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/XXX.WebAPI"><Demo><Y>2</Y></Demo><Demo><Y>0</Y></Demo><Demo><Y>1</Y></Demo></ArrayOfDemo>
JSON (Accept header set to application/json)
[{"X":3,"Y":2},{"X":3,"Y":0},{"X":3,"Y":1}]
My question is quite simple: Why is the X variable not serialized with the XML version (I thought that readonly properties were serialized) and more important, why in both cases is the Name property (which is writable) not serialized??
What are the alternatives to make this work like I expected?
Edit:
Please, note that I'm in a WebAPI context! By default, the XmlSerializer is automatically set to XmlMediaTypeFormatter and the JSONSerializer to JsonMediaTypeFormatter
This seems to be a bug... using the following workaround made the trick:
public class ListWrapper<T>
{
public ListWrapper(INamedEnumerable<T> list)
{
List = new List<T>(list);
Name = list.Name;
}
public List<T> List { get; set; }
public string Name { get; set; }
}
XML serializers only allows serialization of properties with "set" provided.
What are you using to serialize it? If you don't need attributes you could use DataContractSerializer as mentioned here. By default properties without a set are not serialized however using DataContractSerializer or implementing IXmlSerializable should do the trick for you.
using System;
using System.Runtime.Serialization;
using System.Xml;
[DataContract]
class MyObject {
public MyObject(Guid id) { this.id = id; }
[DataMember(Name="Id")]
private Guid id;
public Guid Id { get {return id;}}
}
static class Program {
static void Main() {
var ser = new DataContractSerializer(typeof(MyObject));
var obj = new MyObject(Guid.NewGuid());
using(XmlWriter xw = XmlWriter.Create(Console.Out)) {
ser.WriteObject(xw, obj);
}
}
}
I'm attempting to serialize (and subsequently deserialize) a rather simple class to an XML string, but am getting an exception: "The type System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] may not be used in this context."
The method I'm using to serialize is:
public string ToXml(TaskListFilterConfig config)
{
Type[] extraTypes = { typeof(FilterConfig), typeof(SortConfig) };
XmlSerializer serializer = new XmlSerializer(config.GetType(), extraTypes);
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, config);
return writer.ToString();
}
}
The classes I'm attempting to serialize are:
[XmlRoot(ElementName = "TaskListFilterConfig", IsNullable = false)]
[XmlInclude(typeof(FilterConfig))]
[XmlInclude(typeof(SortConfig))]
public class TaskListFilterConfig
{
[XmlArray("FilterConfigList")]
[XmlArrayItem("FilterConfig")]
public List<FilterConfig> FilterConfigList { get; set; }
[XmlArray("SortConfigList")]
[XmlArrayItem("SortConfig")]
public List<SortConfig> SortConfigList { get; set; }
public TaskListFilterConfig()
{
FilterConfigList = new List<FilterConfig>();
SortConfigList = new List<SortConfig>();
}
}
[XmlType("FilterConfig")]
public class FilterConfig
{
public OperandType Operand { get; set; }
public int SelectedOperatorIndex { get; set; }
public int SelectedColumnIndex { get; set; }
public object RightOperand { get; set; }
public FilterConfig() { }
}
[XmlType("SortConfig")]
public class SortConfig
{
public Infragistics.Windows.Controls.SortStatus SortDirection { get; set; }
public int ColumnSelectedIndex { get; set; }
public SortConfig() { }
}
Your Class Model and Searalizable data are wrong.
Notice that TaskListFilterConfig is a different type than extraTypes. extraTypes has some other model data.
If you want to serialize TaskListFilterConfig class and Data: Use below Code
public string ToXml(TaskListFilterConfig config)
{
XmlSerializer serializer = new XmlSerializer(typeOf(TaskListFilterConfig));
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, config);
return writer.ToString();
}
}
Or if you want extraTypes pass the relevent model data as well.
I know it's a very late answer. Just want to make sure the question has answered. Maybe it help will someone in the future. :)