1

I have an array of Fruit objects, some of them Oranges, some of them Apples.
I would like to serialize them to a list that looks like:

<Fruits>
    <AppleFruit>
         <IsRotten>true</IsRotten>
         <FellFarFromTree>false</FellFarFromTree>
    </AppleFruit>
    <OrangeFruit>
         <IsRotten>false</IsRotten>
         <NumberOfSegments>6</NumberOfSegments>
    </OrangeFruit>
</Fruits>

So I'm trying the following:

[Serializable]
[XmlInclude(typeof(Apple))]
[XmlInclude(typeof(Orange))]
public abstract class Fruit {
    public bool IsRotten { get; set; }
}

[Serializable]
[XmlRoot("AppleFruit")]
public class Apple : Fruit {
    public bool FellFarFromTree { get; set; }
}

[Serializable]
[XmlRoot("OrangeFruit")]
public class Orange : Fruit {
    public int NumberOfSegments { get; set; }
}

public class Blender {
    public void XmlBlend(params Fruit[] fruits) {
        using (var writer = new XmlTextWriter(@"c:\test\blended_fruits.xml", Encoding.UTF8)) {
            writer.Formatting = Formatting.Indented;
            writer.WriteStartDocument();
            writer.WriteStartElement("Fruits");

            var serializer = new XmlSerializer(typeof (Fruit));

            foreach (var fruit in fruits) {
                serializer.Serialize(writer, fruit);
            }

            writer.WriteEndElement();
            writer.WriteEndDocument();
        }
    }

    [Test]
    public void TestIt () {
        var blender = new Blender();
        blender.XmlBlend(
            new Apple() {
                FellFarFromTree = false,
                IsRotten = true
            },
            new Orange() {
                IsRotten = false,
                NumberOfSegments = 6
            });
    }
}

But the XmlRoot attribute seems to be totally ignored. The actual output comes out looking like:

<Fruits>
  <Fruit xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Apple">
    <IsRotten>true</IsRotten>
    <FellFarFromTree>false</FellFarFromTree>
  </Fruit>
  <Fruit xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Orange">
    <IsRotten>false</IsRotten>
    <NumberOfSegments>6</NumberOfSegments>
  </Fruit>
</Fruits>

What am I missing?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Mike Ruhlin
  • 3,546
  • 2
  • 21
  • 31
  • possible duplicate of [XmlRoot() for Xml Serilization does not work](http://stackoverflow.com/questions/1440845/xmlroot-for-xml-serilization-does-not-work) (Try `XmlType()`) – Bobson May 05 '14 at 19:34
  • `XmlType("AppleFruit")` just changes the `xsi:type` to `"AppleFruit"` – Tim S. May 05 '14 at 19:37
  • Close, but no cigar. That yields (note that the xsi:type attribute has changed, but not the node name) – Mike Ruhlin May 05 '14 at 19:37
  • @MikeRuhlin - Do any of the other suggestions from that answer (`XmlElement()`, using `XmlAttributeOverrides`), help? – Bobson May 05 '14 at 19:47
  • Nope. XmlElement() is for cases where you want to give all the elements in the list the same node name. XmlAttributeOverrides does same behavior as what I already have. – Mike Ruhlin May 05 '14 at 19:56

2 Answers2

1

Answering with my own workaround, but if somebody has a better answer I'll accept it.

I created a different serializer for each class and stuck them in a dictionary:

public Dictionary<Type, XmlSerializer> ShouldntHaveToDoThis = new Dictionary<Type, XmlSerializer>() {
    {typeof(Apple), new XmlSerializer(typeof(Apple))},
    {typeof(Orange), new XmlSerializer(typeof(Orange))}
};

then get the appropriate serializer for each item:

        foreach (var fruit in fruits) {
            var serializer = ShouldntHaveToDoThis[fruit.GetType()];
            serializer.Serialize(writer, fruit);
        }
Mike Ruhlin
  • 3,546
  • 2
  • 21
  • 31
0

One way to do this is to create a type for Fruits with a list of each type of Fruit and use the XmlElement attribute to name the items.

[XmlRoot("Fruits")]
public class Fruits
{
    [XmlElement("AppleFruit")]
    public Apple[] Apples { get; set; }
    [XmlElement("OrangeFruit")]
    public Orange[] Oranges { get; set; }
}
[Serializable]
[XmlInclude(typeof(Apple))]
[XmlInclude(typeof(Orange))]
public abstract class Fruit {
    public bool IsRotten { get; set; }
}

[Serializable]
public class Apple : Fruit {
    public bool FellFarFromTree { get; set; }
}

[Serializable]
public class Orange : Fruit {
    public int NumberOfSegments { get; set; }
}


public void XmlBlend(Fruits fruits) {
    using (var writer = new XmlTextWriter(@"c:\test\blended_fruits.xml", Encoding.UTF8)) {
        writer.Formatting = Formatting.Indented;

        var serializer = new XmlSerializer(typeof(Fruits));
        serializer.Serialize(writer, fruits);
    }
}

Produces output like:

<?xml version="1.0" encoding="utf-8"?>
<Fruits xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <AppleFruit>
    <IsRotten>true</IsRotten>
    <FellFarFromTree>false</FellFarFromTree>
  </AppleFruit>
  <OrangeFruit>
    <IsRotten>false</IsRotten>
    <NumberOfSegments>6</NumberOfSegments>
  </OrangeFruit>
</Fruits>

Having to list out Apple[] Apples, etc. isn't exactly pretty, but I see it as akin to needing [XmlInclude(typeof(Apple))] on Fruit.

Tim S.
  • 55,448
  • 7
  • 96
  • 122