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

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.

Related

Deserializing JSON to a class containing a dynamic property with System.Text.Json

The objective is to deserialize a JSON response to a wrapper response class containing a dynamic part, using the new System.Text.Json library from NET Core 3.
That is
{
"fixedProperty": "Hello",
"dynamicProperty": {
"attributeOne": "One",
"attributeTwo": "Two",
}
}
to
public class MyResponseClass
{
public string FixedProperty { get; set; }
public dynamic DynamicProperty { get; set; }
}
// Where the dynamic property is one of the classes.
// (MyDataClassOne in the particular JSON example above)
public class MyDataClassOne
{
public string AttributeOne { get; set; }
public string AttributeTwo { get; set; }
}
public class MyDataClassTwo
{
public string AttributeThree { get; set; }
public string AttributeFour { get; set; }
}
...
The type of the dynamic property in the response is always known in advance (depends on the request), and is one of, say, three different classes.
Could not figure out a clean way to do so, except for not having one wrapper class with a dynamic property but multiple distinct response classes for each of the cases (which obviously works fine but is not the desired solution).
EDIT:
The solution was to use a generic.
How about something like this?
var myResponseClass = new MyResponseClass();
dynamic myClass = JsonSerializer.Deserialize<ExpandoObject>("{\"fixedProperty\":\"Hello\",\"dynamicProperty\": {\"attributeOne\":\"One\",\"attributeTwo\":\"Two\"}}");
dynamic myProperty = JsonSerializer.Deserialize<ExpandoObject>(myClass.dynamicProperty.ToString());
myResponseClass.FixedProperty = myClass.fixedProperty.ToString();
myResponseClass.DynamicProperty = myProperty;
Since the type of the dynamic property in the response is always known in advance (depends on the request), you can use a generic root object:
public class MyResponseClass<T>
{
public string FixedProperty { get; set; }
public T DynamicProperty { get; set; }
}
And then declare T to be whatever known concrete class is required, e.g.
var root = JsonSerializer.Deserialize<MyResponseClass<MyDataClassOne>>(responseString);
var fixedProperty = root.fixedProperty;
var attributeOne = root.DynamicProperty?.AttributeOne;

c# deserializate xml embedded node to class property

i have part of xml document
<Tender SubTenderType="BC" TenderType="Check">
<TenderTotal>
<Amount>10.00</Amount>
</TenderTotal>
</Tender>
i need convert it to class.
public class Tender
{
public string SubTenderType { get; set; }
public string TenderType { get; set; }
public decimal Amount { get; set; }
}
what i already wrote and this work. but i interseing can i deserialize xml to class as written above?
[Serializable]
public class Tender
{
[XmlAttribute("SubTenderType")]
public string SubTenderType { get; set; }
[XmlAttribute("TenderType")]
public string TenderType { get; set; }
[XmlElement("TenderTotal")]
public TenderTotal TenderTotal { get; set; }
}
[Serializable]
public class TenderTotal
{
[XmlElement("Amount")]
public decimal Amount { get; set; }
}
You can deserialize xml to first Type "Tender" and next use autoMapper to map your type (create new object of different type)
create map:
config.CreateMap<TenderFirst, TenderSecond>().ForMember(x => x.TenderTotal.Amount, y => y.Amount ())
Having the following class without XmlAttribute:
public class Tender
{
public string SubTenderType { get; set; }
public string TenderType { get; set; }
public decimal Amount { get; set; }
}
You can use the XmlAttributeOverrides class to override the behavior of the serializer in such a way that instead of elements it would do the attributes.
var attrSTT = new XmlAttributes { XmlAttribute = new XmlAttributeAttribute("SubTenderType") };
var attrTT = new XmlAttributes { XmlAttribute = new XmlAttributeAttribute("TenderType") };
var overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Tender), nameof(Tender.SubTenderType), attrSTT);
overrides.Add(typeof(Tender), nameof(Tender.TenderType), attrTT);
var xs = new XmlSerializer(typeof(Tender), overrides);
However, in this way impossible add a new item or wrap one element in another.
Therefore, you have to do custom serialization, or map one type to another, or writing a custom xml reader/writer, or perform the read/write manually (for example, using linq2xml). There are many ways...

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

XML Serialization of an Interface

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,

Categories