3

I have the following xml file:

<MyConfig>
  <Item a1="Attrib11" a2="Attrib21" a3="Attrib31" />
  <Item a1="Attrib12" a2="Attrib22" a3="Attrib32" />
</MyConfig>

I load it in using the following helper methods:

public static T Load<T>(string path)
{
    XmlSerializer xml = new XmlSerializer(typeof(T));

    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
    using (StreamReader sr = new StreamReader(fs))
    {
        return (T)xml.Deserialize(sr);
    }
}

public static void Save<T>(string path, T contents)
{
    XmlSerializer xml = new XmlSerializer(typeof(T));

    using (FileStream fs = new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.Read))
    using (StreamWriter sw = new StreamWriter(fs))
    {
        xml.Serialize(sw, contents, ns);
    }
}

This is MyConfig:

public class MyConfig
{
    [XmlElement("Item")]
    public List<Item> Items { get; set; }

    public MyConfig()
    {
        Items = new List<Item>();
    }
}

public class Item : IXmlSerializable
{
    [XmlAttribute()]
    public string Attrib1 { get; set; }

    [XmlAttribute()]
    public string Attrib2 { get; set; }

    [XmlAttribute()]
    public string Attrib3 { get; set; }

    public Item(string attrib1, string attrib2, string attrib3)
    {
        Attrib1 = attrib1;
        Attrib2 = attrib2;
        Attrib3 = attrib3;
    }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        if (reader.MoveToContent() == XmlNodeType.Element)
        {
            Attrib1 = reader.GetAttribute("a1");
            Attrib2 = reader.GetAttribute("a2");
            Attrib3 = reader.GetAttribute("a3");
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteAttributeString("a1", Attrib1);
        writer.WriteAttributeString("a2", Attrib2);
        writer.WriteAttributeString("a3", Attrib3);
    }
}

I then have the following test bed code for checking the serialization of the class:

string file = "somePath";

MyConfig myConfig = new MyConfig()
{
    Items = new List<Item>()
    {
        new Item("Attrib11", "Attrib21", "Attrib31"),
        new Item("Attrib12", "Attrib22", "Attrib32"),
    },
};

Save(file, myConfig);

MyConfig myConfig2 = Load<MyConfig>(file);

This fails with an OutOfMemory exception at Load, how can I fix this? Checking the file at somePath and it looks correct.

TheLethalCoder
  • 6,668
  • 6
  • 34
  • 69
  • In your `ReadXml()` do you correctly consume the outer element? See [Proper way to implement IXmlSerializable?](https://stackoverflow.com/q/279534) and https://www.codeproject.com/Articles/43237/How-to-Implement-IXmlSerializable-Correctly – dbc Oct 09 '17 at 14:42
  • @dbc I have put all the relevant code in here so you tell me, this is my first time doing a class that uses `IXmlSerializable` so I'm not quite sure what you mean. I read all of the attributes in though if that's what you mean. – TheLethalCoder Oct 09 '17 at 14:44

1 Answers1

2

You need to tell the reader to advance to the next node after you've read the attributes:

public void ReadXml(XmlReader reader)
{
    if (reader.MoveToContent() == XmlNodeType.Element)
    {
        Attrib1 = reader.GetAttribute("a1");
        Attrib2 = reader.GetAttribute("a2");
        Attrib3 = reader.GetAttribute("a3");
    }
    // Go to the next node.
    reader.Read();
}

If you don't call reader.Read(), the reader reads the same node over and over again, and therefore the XmlSerializer will create an unlimited amount of Item instances until you finally get the OutOfMemoryException.

haindl
  • 3,111
  • 2
  • 25
  • 31
  • @TheLethalCoder Most likely it would be the same as `reader.MoveToNext()` but `XmlReader` doesn't have such a method. All the `reader.MoveToXXX()` methods are just moving the `reader` within the current node but they don't advance the `reader` to the next node. – haindl Oct 09 '17 at 15:12
  • 1
    Not the best named method then in my opinion, it isn't too obvious what it actually does. – TheLethalCoder Oct 09 '17 at 15:29
  • @TheLethalCoder I absolutely agree! All of these methods are available since .NET 1.1 without any significant changes. And there surely are a few more of those ancient parts in the .NET framework that don't make much sense if you look at them from a modern point of view. – haindl Oct 09 '17 at 15:54