XmlSerializer抛出类型的异常,之后我使用XMLInclude?

时间:2011-11-10 16:06:05

标签: c# xml exception serialization

我得到了一个例外

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

当我尝试序列化我的对象时。我知道这个例外有两种不同的解决方案,但在这种情况下都不起作用。

某些背景:

我有一个严格遵循GoogleEarth / OpenGIS KML format的类结构(用于在GoogleEarth上绘图)。

我的根类型为KMLDocument,其中包含一组KMLObjects

public class KMLDocument
{
    public KMLObject[] members;
}

KMLObjectFeature的基本类型,Placemark

的基类型

问题:

当我为KMLDocument构造序列化程序时,它不会直接知道像Placemark这样的派生类型,除非我明确告诉它。所以我这样做:

XmlSerializer serializer = new XmlSerializer(typeof(KMLDocument),
    new Type[] { typeof(KMLObject),
                 typeof(Feature),
                 typeof(Placemark) } );

我还将属性附加到KMLDocument类,以确保它知道所有重要类型:

[XmlRootAttribute("kml", Namespace="http://www.opengis.net/kml/2.2")]
[XmlInclude(typeof(KMLObject))]
[XmlInclude(typeof(Feature))]
[XmlInclude(typeof(Placemark))]
public class KMLDocument
{  ....   }

但是,尽管我打电话时告诉序列化器有两种不同的方式Placemark 序列化,我得到例外:

static void Main(string[] args)
{
    KMLDocument kml = new KMLDocument();
    kml.AddPlacemark("MyPlacemark", "MyTest");

    XmlSerializer serializer = new XmlSerializer(typeof(KMLDocument),
        new Type[] { typeof(KMLObject),
                     typeof(Feature), 
                     typeof(Placemark) } );
     serializer.Serialize(new StreamWriter("MyKML.kml"), kml);  // Exception on this line!
}

如果我添加一个类型为Placemark的虚拟变量,突然序列化程序可以找到该类型,并且它可以正常工作:

public class KMLDocument
{
    public KMLObject[] members;
    public Placemark dummy_var;  // Should NOT be needed!
}

我错过了什么?为什么两者我的XmlSerializer-Constructor和我的属性未能提供重要信息?

3 个答案:

答案 0 :(得分:3)

地标和要素是KMLObject的子类。 members字段包含地标和要素的混合数组。

members字段必须用XmlArrayItemAttribute标记,以指定它包含的元素是多态的。

http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlarrayitemattribute.aspx

答案 1 :(得分:0)

假设我正确地阅读此内容KMLObjectFeaturePlacemark继承的基础对象。 AddPlacemark()创建Placemark个实例,并将其添加到members数组中。

这应该有助于

public class KMLDocument
{
    [XmlElement("Placemark", Type=typeof(Placemark)),
    XmlElement("Feature", Type=typeof(Feature))]
    public KMLObject[] members;
}

您不应该需要添加到序列化程序或文档中的任何缩写。我还要将members声明为List<KMLObject>而不是数组。

答案 2 :(得分:0)

嗯,那很奇怪。这个完整的例子似乎工作正常(使用VS2008和.NET 3.5)。

public class KMLDocument
{
   public List<KMLObject> members = new List<KMLObject>();

   public void AddPlacemark(string p, string v)
   {
      members.Add(new Placemark(p, v));
   }

   public void AddFeature()
   {
      members.Add(new Feature());
   }
}

public class KMLObject { }
public class Feature : KMLObject { }
public class Placemark : Feature
{
   public string p;
   public string v;
   public Placemark() { }
   public Placemark(string p1, string v1) { p = p1; v = v1; }
}

然后我用以下内容运行它:

KMLDocument kml = new KMLDocument();
kml.AddFeature();
kml.AddPlacemark("MyPlacemark", "MyTest");

XmlSerializer serializer = new XmlSerializer(typeof(KMLDocument),
   new Type[] { typeof(KMLObject),
                typeof(Feature),
                typeof(Placemark) });
serializer.Serialize(new StreamWriter("MyKML.kml"), kml); 

然后它会生成以下内容:

<?xml version="1.0" encoding="utf-8"?>
<KMLDocument ...>
  <members>
    <KMLObject xsi:type="Feature" />
    <KMLObject xsi:type="Placemark">
      <p>MyPlacemark</p>
      <v>MyTest</v>
    </KMLObject>
  </members>
</KMLDocument>

<强>更新 您可以使用XmlAttributeOverrides强制XML创建Feature或Placemark值而不是KMLObject。下面是使用我上面的代码序列化的样子。

XmlAttributes attrs = new XmlAttributes();

XmlElementAttribute attr = new XmlElementAttribute();
attr.ElementName = "Feature";
attr.Type = typeof(Feature);
attrs.XmlElements.Add(attr);

XmlElementAttribute attr2 = new XmlElementAttribute();
attr2.ElementName = "Placemark";
attr2.Type = typeof(Placemark);
attrs.XmlElements.Add(attr2);

XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
attrOverrides.Add(typeof(KMLDocument), "members", attrs);

KMLDocument kml = new KMLDocument();
kml.AddFeature();
kml.AddPlacemark("MyPlacemark", "MyTest");

XmlSerializer serializer = new XmlSerializer(typeof(KMLDocument),
   attrOverrides);
serializer.Serialize(new StreamWriter("MyKML2.kml"), kml); 

这将生成以下XML输出:

<?xml version="1.0" encoding="utf-8"?>
<KMLDocument ...>
  <Feature />
  <Placemark>
    <p>MyPlacemark</p>
    <v>MyTest</v>
  </Placemark>
</KMLDocument>