Error with XML deserialization an IEnumerable class

1.3k views Asked by At

I'm trying to serial and deresial HistoryRoot class to this XML format:

<?xml version="1.0"?>
<HistoryRoot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Files>
    <HistoryItem d="2015-06-21T17:40:42" s="file:///D:\cars.txt" />
  </Files>
  <Folders>
    <HistoryItem d="2015-06-21T17:40:42" s="D:\fc\Cars" />
  </Folders>
</HistoryRoot>

Here is HistoryRoot, HistoryList and HistoryItem class:

[Serializable]
public class HistoryRoot
{
    public HistoryList
    Files = new HistoryList
    {
        sl = new SortedList<DateTime, string>(),
        list = new List<HistoryItem>(),
        max = 500,
        c = program.M.qFile
    },
    Folders = new HistoryList
    {
        sl = new SortedList<DateTime, string>(),
        list = new List<HistoryItem>(),
        max = 100,
        c = program.M.qFolder
    },
}

[Serializable]
public class HistoryList : IEnumerable
{
    [XmlIgnore]
    public List<HistoryItem> list;

    [XmlIgnore]
    public SortedList<DateTime, string> sl;

    [XmlIgnore]
    public int max;

    [XmlIgnore]
    public ComboBox c;

    public IEnumerator GetEnumerator()
    {
        if (list == null) list = new List<HistoryItem>();
        return list.GetEnumerator();
    }
}

public struct HistoryItem
{
    [XmlAttribute("d")]
    public DateTime D;

    [XmlAttribute("s")]
    public string S;
}

This is where I get the error:

using (FileStream fs = new FileStream("filepath.xml", FileMode.Open))
    {
        XmlSerializer serializer = new XmlSerializer(typeof(HistoryRoot));
        HistoryRoot h = (HistoryRoot)serializer.Deserialize(fs);
    }

"There was an error reflecting type 'History.HistoryRoot'." System.Exception {System.InvalidOperationException}
How I can fix this error? Thank!

2

There are 2 answers

0
dbc On BEST ANSWER

In order to serialize or deserialize a class that implements IEnumerable using XmlSerializer, your class must have an Add method. From the documentation:

The XmlSerializer gives special treatment to classes that implement IEnumerable or ICollection. A class that implements IEnumerable must implement a public Add method that takes a single parameter. The Add method's parameter must be of the same type as is returned from the Current property on the value returned from GetEnumerator, or one of that type's bases.

You must have this method even if you never deserialize and only serialize, because XmlSerializer does run-time code generation for both serialization and deserialization at the same time.

The method doesn't actually have to work for serialization to succeed, it just needs to be present:

    public void Add(object obj)
    {
        throw new NotImplementedException();
    }

(Of course, for deserialization to succeed, the method must needs be implemented.)

0
NoName On

Though sbc's answer is correct and I accepted it, I now change the HistoryList class to this make it more easier:

public class HistoryList : List<HistoryItem> //   <-- Add List<HistoryItem>
{    
    [XmlIgnore]
    public SortedList<DateTime, string> sl;

    [XmlIgnore]
    public int max;

    [XmlIgnore]
    public ComboBox c;
}

Then change the HistoryRoot to:

[Serializable]
public class HistoryRoot
{
    public HistoryList
    Files = new HistoryList
    {
        sl = new SortedList<DateTime, string>(),
        //list = new List<HistoryItem>(),   <-- Remove this line
        max = 500,
        c = program.M.qFile
    },
    Folders = new HistoryList
    {
        sl = new SortedList<DateTime, string>(),
        //list = new List<HistoryItem>(),   <-- Remove this line
        max = 100,
        c = program.M.qFolder
    },
}