I realize this looks to be an exact duplicate of Using XmlSerializer to serialize derived classes, but I cannot figure out how to get this working following the guidance from that same question:
using System;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace xmlSerializerLab
{
public class Utf8StringWriter : System.IO.StringWriter
{
public override Encoding Encoding => Encoding.UTF8;
}
[XmlRoot(ElementName = "Query", Namespace = "http://www.opengis.net/wfs")]
public class Query
{
[XmlElement(ElementName = "Filter", Namespace = "http://www.opengis.net/ogc")]
public Filter Filter { get; set; }
}
[XmlInclude(typeof(PropertyIsOpFilter))]
[XmlInclude(typeof(PropertyIsEqualToFilter))]
[XmlInclude(typeof(OpFilterBase))]
[XmlInclude(typeof(LiteralFilter))]
[XmlInclude(typeof(Query))]
[Serializable]
public class Filter
{
[XmlElement]
public Filter And { get; set; }
}
public class PropertyIsOpFilter : Filter, IXmlSerializable
{
public Filter LeftOp { get; set; }
public Filter RightOp { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader) { }
public void WriteXml(XmlWriter writer)
{
Program.ToXml(LeftOp, writer);
Program.ToXml(RightOp, writer);
}
}
[XmlRoot("IsEqualTo")]
public class PropertyIsEqualToFilter : PropertyIsOpFilter { }
public class OpFilterBase : Filter, IXmlSerializable
{
public string Op { get; set; }
public object Value { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader) { }
public void WriteXml(XmlWriter writer)
{
if (!String.IsNullOrEmpty(Op))
{
writer.WriteStartElement(Op);
writer.WriteValue(Value);
writer.WriteEndElement();
}
else
{
writer.WriteValue(Value);
}
}
}
public class LiteralFilter : OpFilterBase { }
class Program
{
public static void ToXml(Object o, XmlWriter writer)
{
var inputSerializer = new XmlSerializer(o.GetType(), new Type[] {
typeof(Filter),
typeof(PropertyIsOpFilter),
typeof(PropertyIsEqualToFilter),
typeof(OpFilterBase),
typeof(LiteralFilter),
typeof(Query)
});
inputSerializer.Serialize(writer, o);
}
public static string ToXml(Object o)
{
var inputSerializer = new XmlSerializer(o.GetType());
using (var writer = new Utf8StringWriter())
{
using (var xmlWriter = new XmlTextWriter(writer))
{
ToXml(o, xmlWriter);
}
return writer.ToString();
}
}
static void Main(string[] args)
{
Filter o = new PropertyIsEqualToFilter()
{
LeftOp = new LiteralFilter()
{
Value = 1
},
RightOp = new LiteralFilter()
{
Value = 1
}
};
var query = new Query()
{
Filter = o
};
Console.WriteLine(ToXml(query));
Console.ReadLine();
}
}
}
It results in this exception:
InvalidOperationException: The type xmlSerializerLab.PropertyIsEqualToFilter may not be used in this context. To use xmlSerializerLab.PropertyIsEqualToFilter as a parameter, return type, or member of a class or struct, the parameter, return type, or member must be declared as type xmlSerializerLab.PropertyIsEqualToFilter (it cannot be object). Objects of type xmlSerializerLab.PropertyIsEqualToFilter may not be used in un-typed collections, such as ArrayLists.
As far as I can tell, I need the IXmlSerializable on the PropertyIsOpFilter and OpFilterBase because I'm trying to target a specific XML format described by this schema. But I'm finding that I also have to make the Query class IXmlSerializable.
Here is a sample XML document that I'd like to be able to produce from the model:
<GetFeature xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
service="WFS"
version="1.1.0"
maxFeatures="0" xmlns="http://www.opengis.net/wfs">
<ResultType>Results</ResultType>
<OutputFormat>text/gml; subtype=gml/3.1.1</OutputFormat>
<Query
d2p1:srsName="EPSG:4326" xmlns:d2p1="http://www.opengis.net/ogc">
<d2p1:Filter>
<d2p1:IsEqualTo>
<d2p1:PropertyName>Prop1</d2p1:PropertyName>
<d2p1:Literal>1</d2p1:Literal>
</d2p1:IsEqualTo>
</d2p1:Filter>
</Query>
</GetFeature>
By making the Query class IXmlSerializable and writing a good bit of WriteXml and ReadXml logic I can get it to work, but I'd expect it to work without having to do all that since the XmlRoot and XmlAttribute and XmlElement tags should give enough information to the serializer for it to know which class to instantiate based on the tag name (match ElementName) and certainly how to serialize based on the attributes.