Xml Serializing subclass problem

  • Thread starter Angelos Karantzalis
  • Start date
A

Angelos Karantzalis

Hi guys. I've come across a problem when I tried to serialize a class into
xml, only to discover that the parent class's XML Serialization properties
weren't included in the output xml.

Actually, the class I'm serializing is two steps down in the inheritance
ladder. It's got a parent class which also has a parent class :( All those
classes in the hierarchy are Xml Serializable, and I'd think that it should
be obvious that all attributes/properties of the parents should be
serialized for any given subclass, no ?


Here's the two classes, subclass first, parent classes afterwards:


[XmlRootAttribute(Namespace="", ElementName="Group", IsNullable=false)]
public class Group : CBusLogicalObject
{
private Unit[] m_units = null;
public Group() : base()
{
}
public Group(string strAddress, CBusNode parent)
: base(strAddress, parent) {
}
public Group(string strAddress, string strName, CBusNode parent)
: base(strAddress, strName, parent) {
}
[XmlArrayItem(ElementName="units", Type=typeof(Unit))]
[XmlArray(ElementName="units")]
public Unit[] Units {
get { return m_units;}
set {
// TODO: Add some validation logic here ...
m_units = value;
}
}
}

... and the parent ...

[XmlRootAttribute(Namespace="", ElementName="LObject", IsNullable=false)]
public abstract class CBusLogicalObject : CBusNode
{
public CBusLogicalObject() : base()
{
}
public CBusLogicalObject(string strAddress, CBusNode parent)
: base(strAddress, parent){
m_address = parent.Address+"/"+strAddress;
}
public CBusLogicalObject(string strAddress, string strName, CBusNode parent)
: base(strAddress, strName, parent){
m_address = parent.Address+"/"+strAddress;
}
}

... and the paren't parent ( don't blame me ... :D )

[XmlRootAttribute(Namespace="", ElementName="CBusNode", IsNullable=false)]
public abstract class CBusNode
{
protected string m_address = null;
protected string m_name = null;
private CBusNode _parent = null;
public CBusNode()
{
}
public CBusNode(string address, CBusNode parent) {
this._parent = parent;
this.m_address = address;
}
public CBusNode(string address, string name, CBusNode parent) {
this._parent = parent;
this.m_address = m_address;
this.m_name = name;
}
[XmlAttributeAttribute(AttributeName="Address")]
public string Address {
get { return m_address; }
}
[XmlAttributeAttribute(AttributeName="Name")]
public string Name {
get {return m_name; }
}

Thanks a lot for any help,
Cheers,
Angel
O:]
 
W

William Stacey [MVP]

Start with easier example such as below. Note private fields will not be
serialized. Public fields and public properties with both get and set will
be. Null fields will also not be serialized by default.

DerivedClass dc = new DerivedClass();
string xml = dc.ToXmlString();
Console.WriteLine(xml);

public class BaseClass
{
public string BaseString = "Hello"; // public.
private string notSerialized; // not xmlserialized as private.
private string base2String = "Happy Holidays"; // property is public.

public BaseClass()
{
notSerialized = "not";
Console.WriteLine(notSerialized); // remove compiler warning.
}

public string Base2String
{
get { return base2String; }
set { base2String = value; } // Both get/set required by xmlseralizer.
}
}

public class DerivedClass : BaseClass
{
private static XmlSerializer ser = new XmlSerializer(typeof(DerivedClass));

public string DerivedString = "There";

public DerivedClass() : base()
{
}

public string ToXmlString()
{
byte[] bytes = ToBytes(false);
return Encoding.UTF8.GetString(bytes);
}

public static DerivedClass FromXmlString(string xmlString)
{
if ( xmlString == null )
throw new ArgumentNullException("xmlString");

DerivedClass xr = null;
using(StringReader sr = new StringReader(xmlString))
{
xr = (DerivedClass)ser.Deserialize(sr);
return xr;
}
}

/// <summary>
/// Serialize class to utf-8 encoded byte array. Serialize defaults to
/// using UTF8 so we can avoid an additional XmlWriter stream.
/// </summary>
/// <param name="prefixLen"></param>
/// <returns></returns>
public byte[] ToBytes(bool prefixLen)
{
using(MemoryStream ms = new MemoryStream())
{
if ( prefixLen )
ms.Position = 4;

ser.Serialize(ms, this);

if ( prefixLen )
{
if ( ms.Length > uint.MaxValue )
throw new ArgumentException("Serialized class bigger then
uint.MaxValue");
uint len = (uint)ms.Length;
byte[] lenBytes = BitConverter.GetBytes(len);
ms.Position = 0;
ms.Write(lenBytes, 0, lenBytes.Length);
}
return ms.ToArray();
}
}
}

-- Output --
<?xml version="1.0"?>
<DerivedClass xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<BaseString>Hello</BaseString>
<Base2String>Happy Holidays</Base2String>
<DerivedString>There</DerivedString>
</DerivedClass>

--
William Stacey, MVP
http://mvp.support.microsoft.com

Angelos Karantzalis said:
Hi guys. I've come across a problem when I tried to serialize a class into
xml, only to discover that the parent class's XML Serialization properties
weren't included in the output xml.

Actually, the class I'm serializing is two steps down in the inheritance
ladder. It's got a parent class which also has a parent class :( All those
classes in the hierarchy are Xml Serializable, and I'd think that it should
be obvious that all attributes/properties of the parents should be
serialized for any given subclass, no ?


Here's the two classes, subclass first, parent classes afterwards:


[XmlRootAttribute(Namespace="", ElementName="Group", IsNullable=false)]
public class Group : CBusLogicalObject
{
private Unit[] m_units = null;
public Group() : base()
{
}
public Group(string strAddress, CBusNode parent)
: base(strAddress, parent) {
}
public Group(string strAddress, string strName, CBusNode parent)
: base(strAddress, strName, parent) {
}
[XmlArrayItem(ElementName="units", Type=typeof(Unit))]
[XmlArray(ElementName="units")]
public Unit[] Units {
get { return m_units;}
set {
// TODO: Add some validation logic here ...
m_units = value;
}
}
}

.. and the parent ...

[XmlRootAttribute(Namespace="", ElementName="LObject", IsNullable=false)]
public abstract class CBusLogicalObject : CBusNode
{
public CBusLogicalObject() : base()
{
}
public CBusLogicalObject(string strAddress, CBusNode parent)
: base(strAddress, parent){
m_address = parent.Address+"/"+strAddress;
}
public CBusLogicalObject(string strAddress, string strName, CBusNode parent)
: base(strAddress, strName, parent){
m_address = parent.Address+"/"+strAddress;
}
}

.. and the paren't parent ( don't blame me ... :D )

[XmlRootAttribute(Namespace="", ElementName="CBusNode", IsNullable=false)]
public abstract class CBusNode
{
protected string m_address = null;
protected string m_name = null;
private CBusNode _parent = null;
public CBusNode()
{
}
public CBusNode(string address, CBusNode parent) {
this._parent = parent;
this.m_address = address;
}
public CBusNode(string address, string name, CBusNode parent) {
this._parent = parent;
this.m_address = m_address;
this.m_name = name;
}
[XmlAttributeAttribute(AttributeName="Address")]
public string Address {
get { return m_address; }
}
[XmlAttributeAttribute(AttributeName="Name")]
public string Name {
get {return m_name; }
}

Thanks a lot for any help,
Cheers,
Angel
O:]
 
A

Angelos Karantzalis

Thanks for the reply Will. I think the original problem was caused because
the "parent's parent" class CBusNode had the Address & Name properties
read-only (no set in the properties). I saw that after I'd send the post,
corrected it, but now I get some very weird exception upon running the test
application, about missing a DLL library with cryptic name - plus it throws
the same exception with a different cryptic dll name every time, as if the
dll name were random!

.... gosh, just when I was saying how nice Xml Serialization is in .NET as
opposed to some Java APIs. Guess again ! :?

Cheers,
Angel
O:]


William Stacey said:
Start with easier example such as below. Note private fields will not be
serialized. Public fields and public properties with both get and set will
be. Null fields will also not be serialized by default.

DerivedClass dc = new DerivedClass();
string xml = dc.ToXmlString();
Console.WriteLine(xml);

public class BaseClass
{
public string BaseString = "Hello"; // public.
private string notSerialized; // not xmlserialized as private.
private string base2String = "Happy Holidays"; // property is public.

public BaseClass()
{
notSerialized = "not";
Console.WriteLine(notSerialized); // remove compiler warning.
}

public string Base2String
{
get { return base2String; }
set { base2String = value; } // Both get/set required by xmlseralizer.
}
}

public class DerivedClass : BaseClass
{
private static XmlSerializer ser = new XmlSerializer(typeof(DerivedClass));

public string DerivedString = "There";

public DerivedClass() : base()
{
}

public string ToXmlString()
{
byte[] bytes = ToBytes(false);
return Encoding.UTF8.GetString(bytes);
}

public static DerivedClass FromXmlString(string xmlString)
{
if ( xmlString == null )
throw new ArgumentNullException("xmlString");

DerivedClass xr = null;
using(StringReader sr = new StringReader(xmlString))
{
xr = (DerivedClass)ser.Deserialize(sr);
return xr;
}
}

/// <summary>
/// Serialize class to utf-8 encoded byte array. Serialize defaults to
/// using UTF8 so we can avoid an additional XmlWriter stream.
/// </summary>
/// <param name="prefixLen"></param>
/// <returns></returns>
public byte[] ToBytes(bool prefixLen)
{
using(MemoryStream ms = new MemoryStream())
{
if ( prefixLen )
ms.Position = 4;

ser.Serialize(ms, this);

if ( prefixLen )
{
if ( ms.Length > uint.MaxValue )
throw new ArgumentException("Serialized class bigger then
uint.MaxValue");
uint len = (uint)ms.Length;
byte[] lenBytes = BitConverter.GetBytes(len);
ms.Position = 0;
ms.Write(lenBytes, 0, lenBytes.Length);
}
return ms.ToArray();
}
}
}

-- Output --
<?xml version="1.0"?>
<DerivedClass xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<BaseString>Hello</BaseString>
<Base2String>Happy Holidays</Base2String>
<DerivedString>There</DerivedString>
</DerivedClass>

--
William Stacey, MVP
http://mvp.support.microsoft.com

Angelos Karantzalis said:
Hi guys. I've come across a problem when I tried to serialize a class into
xml, only to discover that the parent class's XML Serialization properties
weren't included in the output xml.

Actually, the class I'm serializing is two steps down in the inheritance
ladder. It's got a parent class which also has a parent class :( All those
classes in the hierarchy are Xml Serializable, and I'd think that it should
be obvious that all attributes/properties of the parents should be
serialized for any given subclass, no ?


Here's the two classes, subclass first, parent classes afterwards:


[XmlRootAttribute(Namespace="", ElementName="Group", IsNullable=false)]
public class Group : CBusLogicalObject
{
private Unit[] m_units = null;
public Group() : base()
{
}
public Group(string strAddress, CBusNode parent)
: base(strAddress, parent) {
}
public Group(string strAddress, string strName, CBusNode parent)
: base(strAddress, strName, parent) {
}
[XmlArrayItem(ElementName="units", Type=typeof(Unit))]
[XmlArray(ElementName="units")]
public Unit[] Units {
get { return m_units;}
set {
// TODO: Add some validation logic here ...
m_units = value;
}
}
}

.. and the parent ...

[XmlRootAttribute(Namespace="", ElementName="LObject", IsNullable=false)]
public abstract class CBusLogicalObject : CBusNode
{
public CBusLogicalObject() : base()
{
}
public CBusLogicalObject(string strAddress, CBusNode parent)
: base(strAddress, parent){
m_address = parent.Address+"/"+strAddress;
}
public CBusLogicalObject(string strAddress, string strName, CBusNode parent)
: base(strAddress, strName, parent){
m_address = parent.Address+"/"+strAddress;
}
}

.. and the paren't parent ( don't blame me ... :D )

[XmlRootAttribute(Namespace="", ElementName="CBusNode", IsNullable=false)]
public abstract class CBusNode
{
protected string m_address = null;
protected string m_name = null;
private CBusNode _parent = null;
public CBusNode()
{
}
public CBusNode(string address, CBusNode parent) {
this._parent = parent;
this.m_address = address;
}
public CBusNode(string address, string name, CBusNode parent) {
this._parent = parent;
this.m_address = m_address;
this.m_name = name;
}
[XmlAttributeAttribute(AttributeName="Address")]
public string Address {
get { return m_address; }
}
[XmlAttributeAttribute(AttributeName="Name")]
public string Name {
get {return m_name; }
}

Thanks a lot for any help,
Cheers,
Angel
O:]
 
W

William Stacey [MVP]

application, about missing a DLL library with cryptic name - plus it
throws
the same exception with a different cryptic dll name every time, as if the
dll name were random!

It is kinda random. A dynamic dll gets created by xmlserializer for your
class. At the moment, I forget why that was necessary or if that step could
be avoided. Would need to see the error message to be sure, but it must be
something to do with issue of serialization or deserialization.
 
A

Angelos Karantzalis

Well, after a whole lot of debugging & playing around & a little help from:
http://weblogs.asp.net/cschittko/articles/33045.aspx I solved the problems.

I just cast the instace being serialized inot the base class in the
XmlSerializer & included the derived classes with an [XmlInclude] attribute
in the parent class.

Thanks for the help William.

Cheers,
Angel
O:]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top