序列化具有相同类型名称的多态List

时间:2013-02-13 22:01:49

标签: c# .net polymorphism xmlserializer

我有一个对象,我正在尝试序列化为XML。在此对象内部是泛型类型(抽象类)的列表。此列表中的每个项可以是不同的类,但所有项都继承自抽象基类:

public abstract class animal
{
  public string type { get; set; }
}
public class cat:animal
{
  public string size { get; set; }
  public string furColor { get; set; }
}
public class fish:animal
{
  public string size { get; set; }
  public string scaleColor { get; set; }
}

当我序列化列表时,我希望它看起来像这样:

<animal type="cat">
  <size>medium</size>
  <furColor>black</furColor>
</animal>
<animal type="fish">
  <size>small</size>
  <scaleColor>silver</scaleColor>
</animal>

我尝试过这个简单的解决方案:

[XmlElement("Animal")]
public List<animal> Animals { get; set; }

但它会抛出错误,因为它不期望对象类型为“cat”。将[XmlInclude]标记添加到基类,派生类或整个包含类(让我们称之为动物园)对此没有帮助。

我可以将typeof指定用于单个类:

[XmlElement("Animal", typeof(cat))]
public List<animal> Animals { get; set; }

只要我只使用猫,这就可以正常使用。再一次,我在混合物中添加一条鱼,它会以相同的错误(不期待鱼)爆炸。

我可以添加多个typeof属性:

[XmlElement("Animal")]
[XmlElementAttribute(typeof(cat))]
[XmlElementAttribute(typeof(fish))]
public List<animal> Animals { get; set; }

并且这会编译,但会忽略元素名称,并将对象序列化为<cat> </cat><fish> </fish>,这是不可接受的。

我甚至尝试添加多个[XmlElement]标签:

[XmlElement("Animal", typeof(cat))]
[XmlElement("Animal", typeof(fish))]
public List<animal> Animals { get; set; }

这个引发了一个不同的例外,这次对象“cat”和“fish”都在同一范围内使用“Animal”类型。

有人能想出解决方法吗?

UPDATE 经过一番挖掘后,我发现This SO post建议将命名空间添加到基类:

[XmlRoot(Namespace="myNamespace")]
[XmlInclude(typeof(cat))]
[XmlInclude(typeof(fish))]
public abstract class animal

序列化产生以下结果:

<animal xsi:type="cat" type="cat">
  ...
</animal>
<animal xsi:type="fish" type="fish">
  ...
</animal>

其中 xsi:type =“cat”是指类的名称, type =“cat”是指在基类中创建的type属性(看到最顶层的例子)。这是如此接近我所需要的,我担心我可能只是在这里缺乏经验,但有没有办法摆脱xsi:type属性列表?

3 个答案:

答案 0 :(得分:3)

将XmlInclude属性添加到基类应解决此序列化问题

    [Serializable]
    [XmlInclude(typeof(cat))]
    [XmlInclude(typeof(fish))]
    class animals
    {
         ....
    }

    public class cat:animals
    {
      public string size { get; set; }
      public string furColor { get; set; }
    }

    public class fish:animals
    {
      public string size { get; set; }
      public string scaleColor { get; set; }
    }

更新

这是一个适用于我的简短示例,您实际上并不需要type属性,因为您可以使用typeof()方法检索它。除非type属性有另一个目的。

        List<animal> animals = new List<animal>();
        cat catofdoom = new cat();
        catofdoom.furColor = "brown";
        catofdoom.size = "10 pounds";
        animals.Add(catofdoom);

        fish fishofdoom = new fish();
        fishofdoom.scaleColor = "blue";
        fishofdoom.size = "12 inches";
        animals.Add(fishofdoom);


        try
        {
            XmlSerializer xs = new XmlSerializer(typeof(List<animal>));
            using (StreamWriter wr = new StreamWriter("animal.xml"))
            {
                xs.Serialize(wr, animals);
            }
        }
        catch (Exception e)
        {

            throw;
        }

结果(非常基本的序列化,没有选项):

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfAnimal xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <animal xsi:type="cat">
    <size>10 pounds</size>
    <furColor>brown</furColor>
  </animal>
  <animal xsi:type="fish">
    <size>12 inches</size>
    <scaleColor>blue</scaleColor>
  </animal>
</ArrayOfAnimal>

答案 1 :(得分:2)

你需要两个!

[Serializable]
[XmlInclude(typeof(cat))]
[XmlInclude(typeof(fish))]
class animals
{
     ....
}

public class cat:animals
{
  public string size { get; set; }
  public string furColor { get; set; }
}

public class fish:animals
{
  public string size { get; set; }
  public string scaleColor { get; set; }
}

请注意XmlElement名称的细微更改。这将正常工作。

[XmlElement("cat", typeof(cat))]
[XmlElement("fish", typeof(fish))]
public List<animal> Animals { get; set; }

答案 2 :(得分:1)

我建议使用DataContractSerializer并将已知类型包含在序列化程序中。使用XmlInclude是encasulation原则的中断。 有关某些样本,请参阅提供的MSDN链接...