0

I have an object model which I need to serialize as XML in order to save the state to a text file. I'm mostly happy with how this is working but I'm stuck trying to serialize an ObservableCollection of ModelRun where ModelRun has several derived types that can be introduced as a result of plug ins (so can't be known at design time).

Here is the code I use to serialize it (this is in an implementation of an interface so has IXmlSerializable implemented - this code is in the WriteXml override):-

if (property.Name == "ModelRuns" || property.Name == "SavedModelRnuns")
                    {
                        writer.WriteStartElement(property.Name);
                        writer.WriteAttributeString("type", property.GetValue(this).GetType().FullName);
                        XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<ModelRun>)); 
                        serializer.Serialize(writer, property.GetValue(this));
                        writer.WriteEndElement();
                    }

Here's a Model Run (simplified)

   public class ModelRun : INotifyPropertyChanged
    {

    public ModelRun()
    { }
    }

and a typical derived model run (simplified)

public class ScenarioModelRun : ModelRun
    {
        public ScenarioConfiguration ScenarioConfiguration { get; set; }
    }

Here is the property ModelRuns (n.b. although this is virtual it hasn't been overridden, I want that to be an option for future plugins though):-

private ObservableCollection<ModelRun> mModelRuns;
    public virtual ObservableCollection<ModelRun> ModelRuns 
    { 
        get { return mModelRuns; } 
        set { mModelRuns = value; ModelRunsUpdated?.Invoke(this, EventArgs.Empty); } 
    }

When I call serializer.Serialize I get an error message:- The type ScenarioModelRun may not be used in this context. To use ScenarioModelRun as a parameter, return type, or member of a class or struct, the parameter, return type, or member must be declared as type ScenarioModelRun (it cannot be object). Objects of type ScenarioModelRun may not be used in un-typed collections, such as ArrayLists.

I've tried implementing IXmlSerializable in ScenarioModelRun, same as I did with the interface implementation above but that hasn't worked. Indeed, the WriteXml method never even got called so my guess is that the error comes back as a result of the serializer not even being able to build the collection.

I've tried adding a reference to my initial plugin project (this won't be an option in the future but thought it was worth a try while I was investigating) so that I can refer to its types and pass those into the serializer:-

XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<ModelRun>), new Type[] { typeof(ScenarioModelRun), typeof(ObservableCollection<ScenarioModelRun>) });

this didn't work either

Any suggestions as to how I should handle this? I thought implementing IXmlSerializable was probably the best way as that allows the consuming class to not know about plugged in types but that's not even resulting in WriteXML getting called so that doesn't help.

  • I don't get it why you are using IXmlSerializable ... [it works out of box](https://dotnetfiddle.net/4PTCf3) – Selvin Oct 18 '22 at 14:59
  • remeber that `ObservableCollection` and `ObservableCollection` are different types there is no inheritance there (generic class doesn't have covariance nor contravariance) – Selvin Oct 18 '22 at 15:00
  • Thanks for the response. That's not working for me. I get the error detailed above. So I guess there must be some other aspect at play. I'll try building a simplified version and see if I can identify what's happening. – FunkyDexter Oct 19 '22 at 07:36
  • OK, I managed to fix it. First off, the reason I'm implementing IXmlSerializable is that the derived types (e.g. ScenarioModelRun, ScenarioConfiguration etc. are introduced via a plugin architecture. So the class that creates the XmlSerialiser cannot pass them in as known types because, at design time, they're not known. During development I was able to add a reference to my own plugin dll so I could test passing them in as known types but that wouldn't be possible in production. – FunkyDexter Oct 19 '22 at 14:07
  • I believe (but haven't yet been able to prove) that the problem I was experiencing is something to do with being inside the implementation of IXmlSerializable.WriteXML. If I try to pass any extra known types in from there I start getting errors back when creating a new XmlSerializer. Every test I did where I was outside that chain worked fine. Take this with a pinch of salt, though, I haven't really proved it yet. Instead I implemented IXmlSerializable on the various plugged in classes and that works fine. It's a little tedious but not the end of the world. – FunkyDexter Oct 19 '22 at 14:16
  • let plugin have method/property which would return serializable types fx `public IEnumerable SerializableTypes {get;}` then you can create array of types with `plugins.SelectMany(p=>p.SerializableTypes).Concat(new[] { ... base types like typeof(ModelRun).. }).ToArray()` and then use it to create `XmlSerializer` such created serializer should be able to deserialize to `ObservableCollection` even derived classes from plugins – Selvin Oct 19 '22 at 14:16

0 Answers0