Search code examples
c#xmlreaderinvalidoperationexceptionixmlserializable

c# Write/Read complex IXmlSerializable class with XmlSerializer fields


I have a class which I would like to serialize. However, this class implements IEnumerator, so I have to make it IXmlSerializable. My class looks like this:

public class Pipeline : IEnumerator<Node>, IEnumerable<Node>, IXmlSerializable
{
    public string Name { get; set; }
    public List<Trigger> triggers;
    public Node root;

    //Methods...
}

The WriteXml-method I came up with is quite straight forward and writes the object (Pipeline), which is representing nodes, some metadata (name, id, etc.) and the edges between the nodes as a file:

public void WriteXml(XmlWriter writer)
{
    writer.WriteAttributeString("Name", Name);

    List<Node> nod = new List<Node>(this);
    var ser1 = new XmlSerializer(typeof(List<Node>), GetLoadedTypes().ToArray());
    ser1.Serialize(writer, nod);

    var ser2 = new XmlSerializer(typeof(List<Edge>));
    ser2.Serialize(writer, GetEdges());
}

The produced XML-file looks ok to me:

<?xml version="1.0" encoding="utf-8"?>
<Pipeline Name="TestPipeline">
  <ArrayOfNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Node xsi:type="TestTrigger" ID="63248778" Name="yesyes" />
    <Node xsi:type="Do" ID="32368095">
      <variables>
        <string>hello</string>
      </variables>
      <Actions>x => Console.WriteLine($"{x}")</Actions>
    </Node>
  </ArrayOfNode>
  <ArrayOfEdge xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Edge From="63248778" To="32368095" />
    <Edge From="32368095" To="" />
  </ArrayOfEdge>
</Pipeline>

But now my troubles are starting: How should the ReadXML-method looks like? What I came up with, ends in the error InvalidOperationException: <Pipeline xmlns=''> was not expected. in the line Node[] nods = (Node[])ser1.Deserialize(reader);.

public void ReadXml(XmlReader reader)
{
    Name = reader.GetAttribute("Name");

    var ser1 = new XmlSerializer(typeof(Node[]));
    Node[] nods = (Node[])ser1.Deserialize(reader);

    //-- load the Edges as well
}

Because the two arrays are quit complex (list of derivatives of Node) and their fields can change in future, I don't want to do this by reading it elementwise and cast it to my the needed objects.

What is the correct solution to read the XML-File again?


Solution

  • As György pointed out in his comment, the missing part was the Read/MoveTo-Command. Here a working example with a nested XmlSerializer:

    public void ReadXml(XmlReader reader)
    {
        Name = reader.GetAttribute("Name");
    
        reader.Read(); // Check if read was successful 
    
        var ser1 = new XmlSerializer(typeof(Node[]), Utils.Utils.GetNodeTypes());
        Node[] nods = (Node[])ser1.Deserialize(reader);
    
        var ser2 = new XmlSerializer(typeof(Edge[]), new Type[] { typeof(Edge) });
        Edge[] edges = (Edge[])ser2.Deserialize(reader);
            
        // Do something with your elements
    }