2013-02-13 5 views
7

Ich habe ein Objekt, das ich versuche, in XML zu serialisieren. In diesem Objekt befindet sich eine Liste eines generischen Typs (abstrakte Klasse). Jeder Eintrag in dieser Liste könnte eine andere Klasse sein, aber alle von der abstrakten Basisklasse erben:Serialisieren Sie eine polymorphe Liste mit demselben Typnamen

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; } 
} 

Wenn ich die Liste Serialisierung, ich will es wie folgt aussehen:

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

I habe die einfache lösung ausprobiert:

Aber es wirft einen Fehler, weil es nicht den Objekttyp "Katze" erwartet. Das Hinzufügen des [XmlInclude] -Tags zu der Basisklasse, der abgeleiteten Klasse oder der gesamten enthaltenden Klasse (nennen wir es zoo) hilft hier nicht.

kann ich die typeof Bezeichnung für eine einzelne Klasse verwenden:

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

und dies funktioniert, wie ich es will, solange ich nur Katzen verwenden. In der Minute, in der ich einen Fisch in die Mischung gebe, explodiert er mit dem gleichen Fehler (ohne Fische zu erwarten).

Ich kann mehrere typeof hinzufügen Attribute:

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

und diese kompiliert, aber ignoriert die Elementnamen und serialisiert die Objekte als <cat> </cat> und <fish> </fish> verbunden, die nicht akzeptabel ist.

ich versucht habe sogar mehr [XmlElement] Hinzufügen von Tags:

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

Dies wirft eine andere Ausnahme, diesmal, dass die Objekte „cat“ und „Fisch“ verwenden beide den Typen „Tier“ in der gleiche Umfang.

Kann mir jemand einen Weg vorstellen?

UPDATE Nach ein wenig mehr zu graben, fand ich This SO post, die den Namespace auf die Basisklasse schlägt vor, fügt hinzu:

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

Serialisierung dies folgende Ausbeuten:

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

Wo xsi: type = "cat" bezieht sich auf den Namen der Klasse und type = "cat" bezieht sich auf das erstellte type-Attribut innerhalb der Basisklasse (siehe das oberste Beispiel). Das ist so nahe zu dem, was ich brauche, und ich fürchte, ich kann nur leiden an Unerfahrenheit hier, aber gibt es eine Möglichkeit, die Xsi: Typ Attributliste loswerden?

+0

Ich habe Ihren Titel bearbeitet. Bitte lesen Sie "[Sollten die Fragen" Tags "in ihren Titeln enthalten?] (Http://meta.stackexchange.com/questions/19190/)", wobei der Konsens "nein, sie sollten nicht" lautet. –

+0

Entschuldigung, ich habe das in den Richtlinien verpasst. Danke für das Heads-up. – JesseF

+0

Eine Teillösung gefunden, siehe das Update unten für Details – JesseF

Antwort

3

das XmlInclude Attribut zu Ihrer Basisklasse Hinzufügen sollte dieses serilazation Problem lösen

[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; } 
    } 

UPDATE

Hier ist ein kurzes Beispiel, das auf meiner Seite funktioniert, müssen Sie nicht wirklich das type-Attribut muss meinen Meinung, weil Sie es mit der Methode typeof() abrufen können. es sei denn, das Attribut type hatte einen anderen Zweck.

 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; 
     } 

Ergebnisse in diese (sehr einfache Serialisierung ohne Optionen):

<?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> 
+0

Ich habe das versucht, und es gibt mir immer noch den Fehler "Der Typ cat wurde nicht erwartet. Verwenden Sie das XmlInclude oder SoapInclude Attribut, um Typen anzugeben, die nicht statisch bekannt sind." Es ist genau wie oben eingegeben. Gibt es noch etwas, das mir fehlt? – JesseF

+0

Ich sollte erwähnen, ich verwende eine .NET 4.5 WebAPI-Lösung. Ich stimme Legrandviking zu, die obige Lösung sollte funktionieren. Machen wir beide etwas falsch? Ich zögere, die "Bug" -Flagge zu schwenken, hat jemand es geschafft, dass so etwas funktioniert? – JesseF

+0

Ich habe meine ursprüngliche Antwort aktualisiert. Ich hoffe, dass dies zu Ihrem Bedarf passt. – legrandviking

1

ich die DataContractSerializer schlage vor, mit und schließen die bekannten Typen in den Serializer. Die Verwendung von XmlInclude ist eine Art Pause für Prinzip. Siehe die angegebene MSDN-Verbindung für einige Beispiele ...

+0

Ich stimme Jaster zu, viel flexibler und erfordert nicht jedes serilisierbare Mitglied, öffentlich zu sein – legrandviking

+0

Hmm ... Dies wird ziemlich viel einen Refactor des gesamten Objekts erfordern ... Hier gehen wir! Ich werde Sie in ein oder zwei Tagen wissen, wenn es funktioniert :) – JesseF

+0

Okay, wenn meine Forschung korrekt ist, kann ich den DataContractSerializer nicht verwenden, weil ich in der Lage sein muss, Attribute zu verwenden, so dass ich mit XMLSerializer feststecke . Das bringt mich dazu zurück, einige der äußeren Attribute loszuwerden. – JesseF

1

Sie brauchen beide!

[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; } 
} 

Beachten Sie die leichte Änderung des XmlElement Namen. Dies wird jetzt korrekt funktionieren.

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

Das beantwortet tatsächlich die Frage. – CSharper

Verwandte Themen