9

我有一个我正在尝试序列化为 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] 标记添加到基类、派生类或整个包含类(我们称之为 zoo)对此没有帮助。

我可以将 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”类型。

谁能想到解决这个问题的方法?

更新经过一番挖掘,我发现这篇 SO 帖子建议将命名空间添加到基类中:

[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"是指在基类中创建的类型属性(参见最上面的示例)。这与我需要的非常接近,我担心我可能只是在这里缺乏经验,但是有没有办法摆脱 xsi:type 属性列表?

4

3 回答 3

4

将 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>
于 2013-02-13T23:19:14.060 回答
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; }
于 2014-09-30T14:10:30.263 回答
1

我建议使用DataContractSerializer并将已知类型包含到序列化程序中。使用 XmlInclude 是对封装原则的一种突破。有关一些示例,请参阅提供的 MSDN 链接...

于 2013-02-14T14:23:39.947 回答