XmlSerializer выдает исключение для типа, после того как я использовал XMLInclude? - PullRequest
1 голос
/ 10 ноября 2011

Я получаю исключение

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

Когда я пытаюсь сериализовать свои объекты.Я знаю о двух разных решениях этого исключения, но ни одно из них не работает в этом случае.

Некоторый фон:

У меня есть Class-структура, которая близко следует за GoogleEarth/ OpenGIS Формат KML (для рисования поверх GoogleEarth).

Мой корневой тип - KMLDocument, который содержит набор KMLObjects:

public class KMLDocument
{
    public KMLObject[] members;
}

И KMLObject является базовым типом для Feature, который является базовым типом для 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 двумя разными способами, когда я вызываю serialize, я получаю исключение:

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 и мои атрибуты не предоставляют важную информацию?

Ответы [ 3 ]

3 голосов
/ 10 ноября 2011

Метка и объект являются подклассами KMLObject. Поле members содержит смешанный массив меток и элементов.

Поле members должно быть помечено XmlArrayItemAttribute, чтобы указать, что содержащиеся в нем элементы являются полиморфными.

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

0 голосов
/ 12 ноября 2011

Хм, это странно. Этот полный пример работает нормально (с использованием 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>
0 голосов
/ 10 ноября 2011

Если я правильно понял, KMLObject - это базовый объект, от которого наследуются Feature и Placemark. И AddPlacemark() создает экземпляр Placemark и добавляет его в массив members.

Это должно помочь тогда

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

Вам не нужны никакие из этих наклонов, которые вы добавили в сериализатор или документ. Я также объявил бы members как List<KMLObject> вместо массива.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...