XML Serialization of an Interface - c#

I have a problem that I'm working in nHibernate project that have the following object:
[Serializable]
public class Prototype
{
public virtual long Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<AttributeGroup> AttributeGroups { get; private set; }
}
I have created a method to deserialize an XML file and put it into object of type Prototype as following :
public static T Deserialize(string fileName)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
XmlTextReader xmlTextReader = new XmlTextReader(fileName);
Object c = xmlSerializer.Deserialize(xmlTextReader);
return (T)c;
}
The problem now is that I have the following exception:
Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericBag`1[BCatalog.Entities.AttributeGroup]' to type 'System.Collections.Generic.List`1[BCatalog.Entities.AttributeGroup]'.
I can't change the type of the IList because of the nHibernate and I want to deserialize the object.
What should I do to solve this problem ?

Interfaces seems to be cumbersome for serialization/deserialization processes. You might need to add another public member to the class that uses a concrete type and mark the interface property as xml ignore. This way you can deserialize the object without loosing your contract base.
Something like the following:
[Serializable]
public class Prototype
{
public virtual long Id { get; private set; }
public virtual string Name { get; set; }
[XMLIgnore]
public virtual IList<AttributeGroup> AttributeGroups {
get { return this.AttributeGroupsList; }
}
public virtual List<AttributeGroup> AttributeGroupsList { get; private set;}
}
For more information about deserialization attributes please check XmlAttributes Properties.
Regards,

Related

Hide specific root object during yaml serialization and show only it's children properties in yaml

I am using one object model
public class MainModel
{
public FirstChildModel firstChild { get; set; }
public SecondChildModel secondChild { get; set; }
}
public class FirstChildModel
{
public string firstProp { get; set; }
public string secondProp { get; set; }
}
public class SecondChildModel
{
public string name { get; set; }
public bool isValid { get; set; }
}
and I am getting it's respective yaml as
But is there any way so that i can hide specific root property and show it's respective children properties like
You could use a custom type converter for this. The documentation shows how to register a type converter when building a serializer.
A custom type converter needs to implement IYamlTypeConverter. In your case, implementing the Accepts and WriteYaml methods should be enough. Depending on which type is responsible for serializing the root, the type converter needs to handle either FirstChildModel or MainModel.

How to write a validation method for derived classes object

I'm reading user input from different types of CSV files having a few common and a few different attributes. I have created a base class TestCaseData and derived classes as below:
public abstract class TestCaseData
{
public abstract string ID { get; set; }
public abstract string Name{ get; set; }
}
public class DerivedClassOne :TestCaseData
{
public override string ID { get; set; }
public override string Name{ get; set; }
pubic string DerivedOneProperty{ get; set; }
}
public class DerivedClassTwo :TestCaseData
{
public override string ID { get; set; }
public override string Name{ get; set; }
pubic string DerivedTwoProperty{ get; set; }
}
I am reading the CSV file and creating a list of derived classes and assigning to list of base class as below
List<TestCaseData> lstTestCaseData = MethodCallToReturnListOf_DerivedOneClassFromCSV();
As now I have lstTestCaseData I have to validate the user inputs also where I am unable to find a way to write a single method to validate user input of type DerivedOneProperty or DerivedTwoProperty as they have their own properties. Anyone can help me here?
I have method signature something like that
public string ValidateCompleteFile(List<TestCaseData> TestCaseInputList, out bool IsInputValid)
You could instead put an abstract validation method on the TestCaseData class and then let each class that inherits this class implement it how they need to.
public abstract class TestCaseData
{
public abstract string ID { get; set; }
public abstract string Name{ get; set; }
public abstract bool Validate();
}
And then call this method for each entry in the TestCaseInputList collection.
The answer regarding an abstract method is the best solution if you're committed to the code pattern you originally conceived of (i.e. calling a validation method on each object). But perhaps it would be better to validate each field in its setter:
public abstract class TestCaseData
{
private string id, name;
public abstract string ID { get; set; }
public abstract string Name{ get; set; }
}
public class DerivedClassOne : TestCaseData
{
public override string ID
{
get { return id; }
set
{
if ( ... ) throw new ArgumentException();
...
id = value;
}
}
...
}
This way an exception is thrown as soon as an invalid value is encountered. Imagine if you created a million of these objects before checking if each one was valid, only to find that the very first one was invalid. This solution avoids a situation like that by proactively validating as the properties are set.

Why am I getting the exception "Consider using a DataContractResolver or add any types not known statically to the list of known types"

I'm trying to serialise a object to Xml using the DataContractSerializer. I have the following classes;
[ActiveRecord(Lazy = true)]
[KnownType(typeof(RoomType))]
[DataContract]
public class Room : ActiveRecordBase<Room>
{
[PrimaryKey]
[DataMember]
public virtual Int64 RoomId { get; protected set; }
[BelongsTo("RoomTypeId")]
[DataMember]
public virtual RoomType RoomType { get; set; }
[Property]
[DataMember]
public virtual Int64 HotelId { get; set; }
[Property]
[DataMember]
public virtual string Name { get; set; }
[Property]
[DataMember]
public virtual string Description { get; set; }
public static Room[] FindByHotelId(Int64 HotelId)
{
return (Room[])FindAllByProperty(typeof(Room), "HotelId", HotelId);
}
}
The RoomType class is
[ActiveRecord(Lazy = true)]
[DataContract]
public class RoomType : ActiveRecordBase<RoomType>
{
[PrimaryKey]
[DataMember]
public virtual int RoomTypeId { get; protected set; }
[Property]
[DataMember]
public virtual string Name { get; set; }
}
I use the following method to serialise the object
internal static XElement ObjectToXElement<T>(T source)
{
XDocument oXDocument = new XDocument();
try
{
using (var writer = oXDocument.CreateWriter())
{
// write xml into the writer
var serializer = new DataContractSerializer(source.GetType());
serializer.WriteObject(writer, source);
}
}
catch(Exception e)
{
using (var writer = oXDocument.CreateWriter())
{
// write xml into the writer
var serializer = new DataContractSerializer(oError.GetType());
serializer.WriteObject(writer, oError);
}
}
return oXDocument.Root;
}
The actual object I'm serialising is;
[KnownType(typeof(List<Room>))]
[KnownType(typeof(RoomType))]
[DataContract]
public class RoomTypeResponse
{
[DataMember]
public int Code { get; set; }
[DataMember]
public string Message { get; set; }
[DataMember]
public List<Room> Rooms { get; set; }
public RoomTypeResponse()
{
this.Rooms = new List<Room>();
}
}
But for some reason when I call the method to serialise the object I get the following exception;
Type 'Castle.Proxies.RoomTypeProxy' with data contract name
'RoomTypeProxy:http://schemas.datacontract.org/2004/07/Castle.Proxies'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.
If I comment out the property in Room class, it works fine
[BelongsTo("RoomTypeId")]
[DataMember]
public virtual RoomType RoomType { get; set; }
I'm not sure why I am getting the exception, because I've added the knowtype attribute for RoomType ? What am I missing, that is causing this problem.
The problem is that one type (Castle.Proxies.RoomTypeProxy) is generated at runtime, so .NET knows nothing about it. This is not an NHibernate-specific problem. If you disable lazy loading and proxy generation, the problem will go away, but I understand it might be difficult.
Other option would be to use another serializer, like BinaryFormatter, but I don't know if that will work for you.

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; }
}

How to serialize two object with `one-to-many` relationship?

I have two classes: Lookup and LookupItem that Lookup has a member named Items that is a collection of LookupItems. I'm not able to serialize Lookup or LookupItem. With the first I get error The type LookupItem was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically. and with the second I got error A circular reference was detected while serializing an object of type Lookup..
How can I solve this problem?
I use following code for serialization:
public static string Serialize(object obj)
{
XmlSerializer ser = new XmlSerializer(obj.GetType());
StringBuilder sb = new StringBuilder();
StringWriter writer = new StringWriter(sb);
ser.Serialize(writer, obj);
return sb.ToString();
}
UPDATE:
skeleton of classes:
[ActiveRecord(Lazy = true)]
public class Lookup : ActiveRecordExtender, IComparable
{
public Lookup()
{
}
[Property]
public virtual string Title { set; get; }
// creating relation
[HasMany(typeof(LookupItem), Cascade = ManyRelationCascadeEnum.All)]
public virtual IList Items { set; get; }
}
[ActiveRecord(Lazy = true)]
public class LookupItem : ActiveRecordExtender
{
public LookupItem()
{
}
//creating relation
[BelongsTo("Lookup_ID")]
public virtual Lookup ContainerLookup { set; get; }
[Property]
public virtual string Title { set; get; }
[Property]
public virtual string Value { set; get; }
[Property]
public virtual int SortOrder { set; get; }
}
Please note I'm using Catle ActiveRecord as my ORM and please notice this problem is not related to inheritance from ActiveRecordBase. Because other classes in this domain that has no relations work properly.
I assume you want to serialize nested classes.
Have a look at similar post
C# System.Xml.Serialization Self-nested elements
http://www.codeproject.com/KB/XML/Serializing_Nested_XML.aspx
According to a blog post and its comments here and if assume there is no need to related data, adding [XmlIgnore] solves the problem.

Categories