0

I've run into an issue with serializing an IEnumerable with the XmlSerializer. Since IEnumerable represents a list of object, XmlSerializer doesn't know ahead of time what types it needs to serialize. But because it needs to know, it throws an InvalidOperationException when it encounters a type other than object.

The type Foo.Bar was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

The XmlIncludeAttribute is intended to be applied to a method to indicate that the return value can be a certain type. However, IEnumerable has no method that I could put the attribute on.

I tried applying them to GetEnumerator:

public class Bar : IEnumerable
{
    private List<object> list;

    public Bar()
    {
    }

    [XmlInclude(typeof(Bar))]
    [XmlInclude(typeof(ChildBar))]
    public IEnumerator GetEnumerator()
    {
        return list.GetEnumerator();
    }

    public void Add(Bar bar)
    {
        list.Add(bar);
    }

    public void Add(ChildBar childBar)
    {
        list.Add(childBar);
    }

    // used for deserialization
    public void Add(object o)
    {
        if (o is Bar || o is ChildBar)
        {
            list.Add(o);
        }
    }

    // more irrelevant stuff
}

public class ChildBar
{
    public ChildBar()
    {
    }

    // more irrelevant stuff
}

That didn't solve it, and I have no idea where else to use the attributes.

Where should I put them? Can I work around it without them? Can I avoid writing my own enumerator?

Kendall Frey
  • 43,130
  • 20
  • 110
  • 148

1 Answers1

0

EDIT: Since their were a couple flaws with my previous answer -didn't have bar containing more bars and didn't use xmlAttributes here is my new solution: This does not implement however...

EDIT: Well I've gone back and reviewed some things and this is my final solution by implementing ICollection, hope this helps anyone trying to find a solution to serializing Collections.

public class Program
{
    static void Main(string[] args)
    {
        var program = new Program();

        program.SerializeObject();
    }

    private int tierIndex = 1;

    public void SerializeObject()
    {
        var barCollection = new BarCollection();
            var bar1 = new Bar() { Name = "bar1" };
            barCollection.Add(bar1);
            var bar2 = new Bar() { Name = "bar2" };
            barCollection.Add(bar2);
                var bar3 = new Bar() { Name = "bar3" };
                bar2.BarCollection.Add(bar3);
                var bar4 = new Bar() { Name = "bar4" };
                bar2.BarCollection.Add(bar4);
            var bar5 = new Bar() { Name = "bar 5" };
            barCollection.Add(bar5);
            var bar6 = new Bar() { Name = "bar 6" };
            barCollection.Add(bar6);
                var bar7 = new Bar() { Name = "bar 7" };
                bar6.BarCollection.Add(bar7);
                    var bar8 = new Bar() { Name = "bar 8" };
                    bar7.BarCollection.Add(bar8);



        var x = new XmlSerializer(typeof(BarCollection));
        x.Serialize(Console.Out, barCollection);

        Console.WriteLine("\n");

        WriteCollection(barCollection);

        Console.ReadLine();
    }

    public void WriteCollection(BarCollection barCollection)
    {
        tierIndex++;

        foreach (Bar bar in barCollection)
        {
            Console.Write(new StringBuilder().Insert(0, "--", tierIndex) + "> ");
            Console.Write(bar.Name + "\n");

            WriteCollection(bar.BarCollection);
        }

        tierIndex--;
    }
}

public class BarCollection : ICollection
{
    private readonly ArrayList barNodes = new ArrayList();

    public Bar this[int index]
    {
        get { return (Bar) barNodes[index]; }
    }

    public void CopyTo(Array a, int index)
    {
        barNodes.CopyTo(a, index);
    }

    public int Count
    {
        get { return barNodes.Count; }
    }

    public object SyncRoot
    {
        get { return this; }
    }

    public bool IsSynchronized
    {
        get { return false; }
    }

    public IEnumerator GetEnumerator()
    {
        return barNodes.GetEnumerator();
    }

    public void Add(Bar bar)
    {
        barNodes.Add(bar);
    }

    public void Add(Object bar)
    {
        barNodes.Add((Bar) bar);
    }
}

public class Bar
{
    [XmlAttribute(AttributeName = "Name")]
    public string Name;

    [XmlArray(ElementName = "BarNodes", IsNullable = true)]
    public BarCollection BarCollection = new BarCollection();
}

Heres the output:

<?xml version="1.0" encoding="IBM437"?>
<ArrayOfBar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <Bar Name="bar1">
    <BarNodes />
  </Bar>

  <Bar Name="bar2">
    <BarNodes>
      <Bar Name="bar3">
        <BarNodes />
      </Bar>

      <Bar Name="bar4">
        <BarNodes />
      </Bar>

    </BarNodes>
  </Bar>

  <Bar Name="bar 5">
    <BarNodes />
  </Bar>

  <Bar Name="bar 6">
    <BarNodes>
      <Bar Name="bar 7">
        <BarNodes>
          <Bar Name="bar 8">
            <BarNodes />
          </Bar>

        </BarNodes>
      </Bar>

    </BarNodes>
  </Bar>

</ArrayOfBar>

----> bar1
----> bar2
------> bar3
------> bar4
----> bar 5
----> bar 6
------> bar 7
--------> bar 8

Another Stack Reference: XmlSerializer won't serialize IEnumerable

Community
  • 1
  • 1
Nate-Wilkins
  • 5,364
  • 4
  • 46
  • 61
  • Doesn't `IXmlSerializable` require that you write all the serialization code yourself? That could be a real pain for me. Is there no easier solution? – Kendall Frey Aug 29 '12 at 16:42
  • Yes the type (T) needs to implement `IXmlSerializable`. With the Read and Write methods they're not that hard to implement just say `writer.WriteStartElement("")` and `writer.WriteAttribute("")` etc. take a look at this too : http://www.codeproject.com/Articles/43237/How-to-Implement-IXmlSerializable-Correctly – Nate-Wilkins Aug 29 '12 at 16:46
  • Yea shouldn't say really easy because every class might have quite a bit of information that you want to write to an XML file. You could also loop through the fields of a class if you want to write all the fields to the XML file. – Nate-Wilkins Aug 29 '12 at 16:53
  • Do you have proof that `XmlSerializer` works inside a `WriteXml` method? The MSDN documentation makes me think it doesn't, since it mentions serializing documents, not elements. – Kendall Frey Aug 29 '12 at 16:54
  • What do you mean by documents and elements? - The XmlWriter writer that your passing in to the WriteXml method is the one Writing elements. – Nate-Wilkins Aug 29 '12 at 16:57
  • Yes, but you pass that writer to an `XmlSerializer`, and I'm not sure that it supports a partly-written document. – Kendall Frey Aug 29 '12 at 16:59
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/15975/discussion-between-dnxviral-and-kendall-frey) – Nate-Wilkins Aug 29 '12 at 16:59