Groups | Blog | Home
all groups > dotnet xml > november 2004 >

dotnet xml : Serializing instances of derived classes



Aleksei Guzev
11/25/2004 3:12:56 PM

Imagine one writing a class library CL1 for data storage.
He defines classes ‘DataItem’ and ‘DataRecord’ so that the latter contains
a collection of the former.
And he derives class ‘IntItem’ from ‘DataItem’

[XmlRoot ("DataItem")]
[XmlInclude (typeof (IntItem))]
public class DataItem
{
public DataItem() {}
public DataItem (string name)
{
_name = name;
}

private string _name;

[XmlAttribute]
public string Name
{
get { return _name; }
set { _name = value; }
}
}

[XmlRoot ("IntItem")]
public class IntItem : SerializeDerivedClass.DataItem
{
public IntItem(){}
public IntItem (string name, int intValue) : base (name)
{
_intValue = intValue;
}

private int _intValue;

[XmlAttribute ("IntValue")]
public int IntValue
{
get { return _intValue; }
set { _intValue = value; }
}
}

[XmlRoot ("DataRecord")]
public class DataRecord
{
public DataRecord() {}

private DataItem [] _data;

[XmlElement ("DataItem", typeof (DataItem))]
[XmlElement ("IntItem", typeof (IntItem))]
public DataItem [] Data
{
get { return _data; }
set { _data = value; }
}
}

Now the following code works fine.

DataRecord rec = new DataRecord ();
rec.Data = new DataItem [3];
rec.Data [0] = new DataItem ("DI-1");
rec.Data [1] = new IntItem ("II-1", 3);

XmlSerializer xser = new XmlSerializer (typeof (DataRecord));

xser.Serialize (Console.Out, rec);

Its output is

<DataRecord>
<DataItem Name="DI-1" />
<IntItem Name="II-1" IntValue="3" />
</DataRecord>

Now another developer creates another class library CL2 containing class
‘DoubleItem’
derived from ‘DataItem’. Of course, the second developer wishes the code
above
including the following line to work too

Rec.Data [2] = new DoubleItem (“DblI-1”, Math.PI);

[XmlRoot ("DoubleItem")]
public class DoubleItem : SerializeDerivedClass.DataItem
{
public DoubleItem(){}
public DoubleItem (string name, double doubleValue) : base (name)
{
_doubleValue = doubleValue;
}

private double _doubleValue;

[XmlAttribute ("DoubleValue")]
public double DoubleValue
{
get { return _doubleValue; }
set { _doubleValue = value; }
}
}

This should produce

<DataRecord>
<DataItem Name="DI-1" />
<IntItem Name="II-1" IntValue="3" />
<DoubleItem Name=”DblI-1” DoubleValue=”3.1415..”/>
</DataRecord>

Can this behavior be achieved without rebuilding the first class library
CL1?
Why 'DoubleItem' is "not expected" while every instance
of 'DoubleItem' is 'DataItem'?

Christoph Schittko [MVP]
11/26/2004 8:30:42 PM
Aleksei,

Are you instantiating the XmlSerializer in CL1 or CL2?

If you are instantiating in CL1, then there is indeed no way to make
this work without rebuilding. Otherwise you could pass the Type
DoubleItem to the constructor of the XmlSerializer:

XmlSerializer xser = new XmlSerializer (typeof (DataRecord), new Type[]
{typeof(DoubleItem)});

However, this will probably not produce the XML format you're looking
for:

<?xml version="1.0" encoding="IBM437"?>
<DataRecord xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w
3.org/2001/XMLSchema-instance">
<DataItem Name="DI-1" />
<IntItem Name="II-1" IntValue="3" />
<DataItem xsi:type="DoubleItem" Name="DblI-1"
DoubleValue="3.1415926535897931"
/>
</DataRecord>

The DoubleItem is serialized as a <DataItem> element.

If you need to customize the format, then you can override the
serialization attributes on the Data property in the DataRecord class.
The code goes like this:

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes dataAttributes = new XmlAttributes();
dataAttributes.XmlElements.Add( new XmlElementAttribute( "DoubleItem",
typeof(DoubleItem) ) );
dataAttributes.XmlElements.Add( new XmlElementAttribute( "DataItem",
typeof(DataItem) ) );
dataAttributes.XmlElements.Add( new XmlElementAttribute( "IntItem",
typeof(IntItem) ) );
overrides.Add( typeof( DataRecord ), "Data", dataAttributes );

XmlSerializer xser = new XmlSerializer (typeof (DataRecord), overrides,
new Type[0], null, "" );

xser.Serialize (Console.Out, rec);

and you get the format you were looking for:

<?xml version="1.0" encoding="IBM437"?>
<DataRecord xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w
3.org/2001/XMLSchema-instance">
<DataItem Name="DI-1" />
<IntItem Name="II-1" IntValue="3" />
<DoubleItem Name="DblI-1" DoubleValue="3.1415926535897931" />
</DataRecord>

Notice here the attributes in the XmlAttributeOverrides collection
override all attributes in the original class. They are not added, to
the existing ones. You have to add the attributes for the DataItem and
IntItem types again, too.

HTH,
Christoph Schittko
MS MVP XML
http://weblogs.asp.net/cschittko

[quoted text, click to view]
Aleksei Guzev
11/29/2004 11:08:04 AM
Thanks. This is just what I was looking for.

On Fri, 26 Nov 2004 20:30:42 -0600, Christoph Schittko [MVP]
[quoted text, click to view]

I am instantiating the serializer in the third module,
which is using both libraries. It knows DoubleItem unlike CL1.

[quoted text, click to view]

If a class is referred in the overrides then it does not need to be
specified
in extraTypes to the serializer constructor.
At first, I've thought "new Type [0]" was an error in Your code ;)

[quoted text, click to view]

But attributes applied to the fields of DataItem etc remain.

Thank You very much.

AddThis Social Bookmark Button