【问题标题】:XmlSerializer throwing exception for type, After I used XMLInclude?XmlSerializer 抛出类型异常,在我使用 XMLInclude 之后?
【发布时间】:2011-11-12 21:37:08
【问题描述】:

我遇到了异常

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 和我的属性未能提供重要信息?

【问题讨论】:

  • 我不明白为什么它不起作用。您是否尝试过调试 XmlSerializer 生成的程序集? hanselman.com/blog/…
  • @Martijn:我一直在寻找生成的程序集,但到目前为止一直找不到。
  • 如 url 中所述,您不需要程序集,pdb en 源代码就可以了。另一种更复杂的方法是将进程转储到内存转储文件并使用 windbg 加载。然后您可以将动态加载的程序集保存到光盘并检查它。

标签: c# xml exception serialization


【解决方案1】:

Placemark 和 Feature 是 KMLObject 的子类。 members 字段包含地标和特征的混合数组。

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

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

【讨论】:

  • 是的,原来需要的是 [XmlArrayItem(typeof(Placemark))]。不幸的是,异常消息根本没有这样暗示。
【解决方案2】:

假设我没看错,KMLObjectFeaturePlacemark 继承的基础对象。而AddPlacemark() 创建一个Placemark 实例并将其添加到members 数组中。

这应该会有所帮助

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

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

【讨论】:

  • members 最初声明为 List,但随着我越来越减少问题,我决定删除泛型问题并替换为一个简单的数组。解决问题后,我将返回列表。
【解决方案3】:

嗯,这很奇怪。这个完整的示例似乎运行良好(使用 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>

【讨论】:

  • 非常有趣。我将使用我的设置(VS2010、.NET4.0)进行调查。但是,在 XML 中,它将内部对象保留为 KMLObject,而不是 Feature 和 Placemark。一个好的 KML 文件要求对象具有正确的类型。
  • @abelenky - 好的,我现在明白了。您可以使用 XmlAttributeOverrides 来完成此操作(请参阅更新)。仅使用 XmlArrayItemAttribute 标记可能更容易,但如果您希望它更具动态性,我会为您提供不同的选项。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-18
  • 2021-06-28
  • 2015-06-16
相关资源
最近更新 更多