Reputation: 3253
I have the class below which I need to serialize using the XML Serializer
I know that the XML serializer does not like interfaces
However, our coding standards normally involve always coding to interfaces not concrete classes
I thought of a way to be able to stream this list without changing the types which would cause many compilation issues, as well as breaking our standards
My idea is to have a property which reads the Meters list and casts to concrete classes then to totally ignore meters when it comes to serializing
However, the XML will still need to have Meters as the element name
However this does not work
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Xml.Serialization;
public class MeterWalkOrder
{
public MeterWalkOrder()
{
Meters = new List<IMeter>();
}
public String Name { get; set; }
[XmlIgnore]
public List<IMeter> Meters { get; set; }
[XmlArrayItem(ElementName = "Meter")]
[XmlElement(ElementName = "Meters")]
public List<Meter> SerializableMeters
{
get
{
return Meters.Cast<Meter>().ToList();
}
set
{
Meters = new List<IMeter>(value);
}
}
}
}
Is there any way around this?
My XML (which cannot be changed) is below
<MeterWalkOrder>
<Name>Red Route</Name>
<Meters>
<Meter>
<MeterID>1</MeterID>
<SerialNumber>12345</SerialNumber>
</Meter>
<Meter>
<MeterID>2</MeterID>
<SerialNumber>SE</SerialNumber>
</Meter>
</Meters>
</MeterWalkOrder>
The top level error is
There was an error reflecting type 'WindowsFormsApplication1.Classes.MeterWalkOrder'.
The inner exception is
{"XmlElement, XmlText, and XmlAnyElement cannot be used in conjunction with XmlAttribute, XmlAnyAttribute, XmlArray, or XmlArrayItem."}
I really would like to avoid setting a precedent for using lists of concrete classes
Upvotes: 1
Views: 2911
Reputation: 43596
You will need to replace [XmlElement(ElementName = "Meters")]
with [XmlArray(ElementName = "Meters")]
as the error states:
{"XmlElement, XmlText, and XmlAnyElement cannot be used in conjunction with XmlAttribute, XmlAnyAttribute, XmlArray, or XmlArrayItem."}
To define an Array for the XmlSerializer
you can mark it with XmlArray
and its corresponding items with XmlArrayItem
Example:
public class MeterWalkOrder
{
public MeterWalkOrder()
{
Meters = new List<IMeter>();
}
public String Name { get; set; }
[XmlIgnore]
public List<IMeter> Meters { get; set; }
[XmlArrayItem(ElementName = "Meter")]
[XmlArray(ElementName = "Meters")]
public List<Meter> SerializableMeters
{
get
{
return Meters.Cast<Meter>().ToList();
}
set
{
Meters = new List<IMeter>(value);
}
}
}
public interface IMeter {
int MeterID { get; set; }
}
public class Meter : IMeter {
public int MeterID { get; set; }
public string SerialNumber { get; set; }
}
Test:
var data = new MeterWalkOrder{ Name = "Red Route", Meters = new List<IMeter>
{
new Meter{ MeterID = 1, SerialNumber = "12345"},
new Meter{ MeterID = 2, SerialNumber = "SE"}
}};
using (var stream = new FileStream("C:\\Users\\Adam\\Desktop\\Test2.xml", FileMode.OpenOrCreate))
{
XmlSerializer ser = new XmlSerializer(typeof(MeterWalkOrder));
ser.Serialize(stream, data);
}
Result:
<?xml version="1.0"?>
<MeterWalkOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Red Route</Name>
<Meters>
<Meter>
<MeterID>1</MeterID>
<SerialNumber>12345</SerialNumber>
</Meter>
<Meter>
<MeterID>2</MeterID>
<SerialNumber>SE</SerialNumber>
</Meter>
</Meters>
</MeterWalkOrder>
Upvotes: 10