title: How to use XmlSerializer to serialize derived instances?
body: <p>I realize this looks to be an exact duplicate of <a href="https://stackoverflow.com/questions/1643139/using-xmlserializer-to-serialize-derived-classes">Using XmlSerializer to serialize derived classes</a>, but I cannot figure out how to get this working following the guidance from that same question:</p>
  
  <pre><code>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 =&gt; 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();
          }
      }
  }
  </code></pre>
  
  <p>It results in this exception:</p>
  
  <blockquote>
    <p>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.</p>
  </blockquote>
  
  <p>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 <a href="https://github.com/ca0v/ogc-lab/blob/master/ogc-models/data/wfs/wfs-110/spec/wfs.xsd" rel="nofollow noreferrer">this schema</a>.  But I'm finding that I also have to make the Query class IXmlSerializable.</p>
  
  <p>Here is a sample XML document that I'd like to be able to produce from the model:</p>
  
  <pre><code>&lt;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"&gt;
    &lt;ResultType&gt;Results&lt;/ResultType&gt;
    &lt;OutputFormat&gt;text/gml; subtype=gml/3.1.1&lt;/OutputFormat&gt;
    &lt;Query
      d2p1:srsName="EPSG:4326" xmlns:d2p1="http://www.opengis.net/ogc"&gt;
      &lt;d2p1:Filter&gt;
        &lt;d2p1:IsEqualTo&gt;
          &lt;d2p1:PropertyName&gt;Prop1&lt;/d2p1:PropertyName&gt;
          &lt;d2p1:Literal&gt;1&lt;/d2p1:Literal&gt;
        &lt;/d2p1:IsEqualTo&gt;
      &lt;/d2p1:Filter&gt;
    &lt;/Query&gt;
  &lt;/GetFeature&gt;
  </code></pre>
  
  <p>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.</p>