none
XmlSerializer: Ausnahme für einzelne Objekte einer Liste RRS feed

  • Frage

  • Hi,

    Ich serialisiere in einer Anwendung ein Objekt, welches eine ObservableCollection von Objekten enthält.

    Jetzt möchte ich zur Laufzeit einzelne Objekte dieser Liste von der Serialisierung ausnehmen, so dass z.B. nur Objekte mit der Property IsSaveable == true ins XML gespeichert werden.

    Jemand eine Idee wie man das elegant machen kann?

    Konnte trotz vielem Lesen und googlen nichts hilfreiches finden.

    Mittwoch, 7. Januar 2015 11:59

Antworten

  • Hi,

    ich persönlich würde, wenn mir das so sehr am Herzen liegt, alle Klassen von einer Schnittstelle ableiten oder eben eine Basisklasse mit dieser Eigenschaft erstellen und alle relevanten Klassen dann von dieser Klasse erben lassen. Mit einem Attribut könnte man dann auch gleich angeben, welche Eigenschaft(en) für die Steuerung der Serialisierung zuständig ist.

    Es gibt verschiedene Mechanismen, mit denen man die (XML) Serialisierung steuern kann, bspw.:

    Private _Age As Int64
    
    <System.Xml.Serialization.XmlIgnoreAttribute> _
    Public AgeSpecified As Boolean = False
    
    <XmlAttribute()> _
    Public Property Age() As Int64
        Get
            Return _Age
        End Get
        Set
            _Age = Value
            If Value > 0 Then
                AgeSpecified = True
            End If
        End Set
    End Property

    Letztendlich kann man sich also auch selbst etwas ähnliches bauen, dass das für Objekte in einer Liste macht. Allerdings wird man sich dafür dann wohl auch einen eigenen Serialisierer erstellen müssen, da die Standardklassen das nicht kennen.

    Wie gesagt, ich sehe deine Anforderung als zu speziell bzw. zu weit gefasst (wenn man die ggfs. in Frage kommenden anderen Anforderungen anderer User berücksichtigt) als dass Microsoft das von Haus aus ins .NET Framework einbauen müsste/sollte.

    Aber da das Framework ja nun Open Source ist, wäre es für dich und andere ja auch möglich, die Idee und den Code dazu beizusteuern. Wer weiß, evtl. findet diese Erweiterung wirklich ihren Weg in die Standardbibliotheken.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Mittwoch, 7. Januar 2015 16:56
    Moderator
  • Hallo,

    Du solltest noch mal darüber nachdenken...

    Die Aufgabe eines Serialisierers ist weder das Filtern, Sortieren oder Verzieren mit Schleifchen... all das widerspricht den SOLID Prinzipien (SRP uam.)

    Hier wäre es sinnvoller Deine Auflistungen, um Methoden zu erweitern, die eine Filterung (sei es fürs Serialisieren oder anderen Gründen) vornehmen. Die könnten z. B. ein IEnumerable<ListType> liefern.

    Auf Instanz-Ebene kann wiederum eine Schnittstelle zur Verallgemeinerung dienen - siehe u. a. Stefans Antwort.

    So bindest Du das Ganze nicht einen spezifischen Serialisierer - ein Tausch ist leichter möglich. Auch eine Nutzung für vergleichbare Aufgaben über die Persistenz hinaus wird dadurch möglich.

    Gruß Elmar

    Mittwoch, 7. Januar 2015 18:36
    Beantworter
  • Hi,

    XmlIgnore eher nicht, da das eine statische Angabe zur Designzeit ist und sich nicht auf einzelne, sondern eben auf alle Instanzen auswirkt. Ob ShouldSerialize den Prinzipien widerspricht, kann ich nicht sagen, könnte aber schon eher sein.

    Mir ist schon klar, dass das, was Du da machen willst, nicht unbedingt ganz abwegig ist und auch ähnlich zu sehen wäre wie ShouldSerialize auf Eigenschaftsebene aber es gibt leider nunmal nichts, was das von dir Gewünschte anbieten würde. Daher wirst Du um eine eigene Logik nicht drumrum kommen.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Mittwoch, 7. Januar 2015 19:18
    Moderator

Alle Antworten

  • Hallo,

    Der XmlSerializer selbst kennt von Haus keine Möglichkeit einzelne Instanzen auszulassen.

    Das einfachste ist es, Du erstellst vor dem Serialisieren eine neue Auflistung - mit ein bissel LINQ sollte das schnell gemacht sein - und verwendest die anstatt der Basis-Auflistung.

    Gruß Elmar

    Mittwoch, 7. Januar 2015 12:40
    Beantworter
  • Schade, das hatte ich befürchtet :(

    Ist halt sehr hässlich, da man dann das object casten muss, original-zustand zwischenspeichern, filtern etc. pp.

    Gibt es einen Xml Serialisierer der da konfigurierbarer ist?

    Mittwoch, 7. Januar 2015 13:24
  • Hallo,
    du hast natürlich die Möglichkeit die IXmlSerializable-Schnittstelle zu implementieren und so das (de)serialisieren komplett selbst zu übernehmen.

    Ich würde (wenn möglich) allerdings auch zu einer neu gefilterten Liste greifen. Das halte ich in den meisten Fällen für das Beste.


    Tom Lambert - .NET (C#) MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Mittwoch, 7. Januar 2015 13:31
    Moderator
  • Hi,

    einen XML Serialisierer, der deine Anforderung unterstützt, kenne ich nicht. Letztendlich müsste der auch ja alles mögliche können, da andere Entwickler auch andere Anforderungen haben.

    Die kannst dir aber doch problemlos mittels FindAll, Select, Where, ... einen Filter bauen und dir dann eine List<T> zurückgeben lassen. Daher verstehe ich nicht, wo das Problem liegen soll.

    ObservableCollection<DeineKlasse> Liste = new ObservableCollection<DeineKlasse>();
    
    Liste.Add( new DeineKlasse() { Name = "Abc", Description = "Hallo Welt" } );
    Liste.Add( new DeineKlasse() { Name = "Def", Description = "Hallo Galaxie" } );
    Liste.Add( new DeineKlasse() { Name = "Xyz", Description = "Hallo Welt" } );
    
    List<DeineKlasse> AusgabeListe = Liste.Where( f => f.Description == "Hallo Welt" ).ToList();

    Damit hast Du in "AusgabeListe" 2 Elemente enthalten. Deine Originalliste bleibt erhalten. "AusgabeListe" übergibst Du dann an die Serialisierungsmethode.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community


    Mittwoch, 7. Januar 2015 15:02
    Moderator
  • Wünschenswert wäre gewesen sowas wie [XmlIgnore] auch zur Laufzeit für Listen machen zu können.

    Das Problem ist das ich fürs Speichern und Laden eine Klasse habe, der man ein Objekt übergibt und dieses dann einfach serialisiert oder deserialisiert wird. Es steht ja im Objekt jeweils drin was gespeichert und was ignoriert werden soll, via [XmlIgnore] etc..

    Das Problem bei der Lösung mit Linq ist, dass man für jeden Spezialfall in dem Konfigurator rum hampeln muss, damit nur das richtige gespeichert wird. Dies ist allerdings der falsche Ort für sowas. Wie ein Objekt gespeichert wird sollte im Objekt stehen. Ich werde daher mal schauen wie man mit dem IXmlSerializable arbeiten kann.

    Ich würde gerne meinen Objekten einfach eine Eigenschaft verpassen, die vorgibt ob das Objekt gespeichert werden soll oder nicht. Siehe z.B. ShouldSerialize für Properties, eben nur für Objekte einer Liste.

    Mittwoch, 7. Januar 2015 15:28
  • Hi,

    deine Anforderung ist aber eben recht speziell bzw. gibt es derart viele Möglichkeiten, die die User dann anwenden könnten, dass es eben sinnlos ist, sowas von Haus aus einzubauen.

    Es gibt keine Standardeigenschaft dafür. Es steht dir natürlich aber frei, eine Basisklasse, Schnittstelle, ... zu erstellen, die eine solche Eigenschaft hat und die dann von deinen Methoden berücksichtigt wird.

    Da die Serialisierung aber alles mögliche serialisieren/deserialisieren kann, also auch Standardklassen, Klassen von Drittanbietern, Listen von Strings, Zahlen, ... die dann verständlicherweise alle keine solchen Eigenschaft hätten, musst Du dir das selbst bauen. Und ich sehe da immer noch kein großes Hindernis, insbesondere, wenn Du eh eigene Methoden für die Serialisierung/Deserialisierung verwendest.

    Du musst auch nicht für jeden "Spezialfall" an irgendeinem Konfigurator (welchen auch immer Du damit meinst) rumbasteln, sondern einfach deine Eigenschaft in alle deine Klassen einbauen und die dann auswerten. Wo soll da das Problem liegen?


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Mittwoch, 7. Januar 2015 15:39
    Moderator
  • Obs jetzt was spezielles ist oder nicht, würde ich jetzt nicht wagen zu beurteilen....

    Hier mal ein gist mit dem hoffentlich klar wird worum es geht: https://gist.github.com/darinkes/c0d079ca676abec1b3d5

    Natürlich ist es kein großer Aufwand mittels Linq ein bisschen Filtern, aber schön ist das nicht. Schöner wäre es, wenn das Objekt immer entscheidet ob und wie es serialisiert wird.

    Dachte nur, nachdem es ShouldSerialize{PropertyName}() für Properties gibt, ob es das dann nicht auf für das ganze Objekt gibt.

    Mittwoch, 7. Januar 2015 16:41
  • Hi,

    ich persönlich würde, wenn mir das so sehr am Herzen liegt, alle Klassen von einer Schnittstelle ableiten oder eben eine Basisklasse mit dieser Eigenschaft erstellen und alle relevanten Klassen dann von dieser Klasse erben lassen. Mit einem Attribut könnte man dann auch gleich angeben, welche Eigenschaft(en) für die Steuerung der Serialisierung zuständig ist.

    Es gibt verschiedene Mechanismen, mit denen man die (XML) Serialisierung steuern kann, bspw.:

    Private _Age As Int64
    
    <System.Xml.Serialization.XmlIgnoreAttribute> _
    Public AgeSpecified As Boolean = False
    
    <XmlAttribute()> _
    Public Property Age() As Int64
        Get
            Return _Age
        End Get
        Set
            _Age = Value
            If Value > 0 Then
                AgeSpecified = True
            End If
        End Set
    End Property

    Letztendlich kann man sich also auch selbst etwas ähnliches bauen, dass das für Objekte in einer Liste macht. Allerdings wird man sich dafür dann wohl auch einen eigenen Serialisierer erstellen müssen, da die Standardklassen das nicht kennen.

    Wie gesagt, ich sehe deine Anforderung als zu speziell bzw. zu weit gefasst (wenn man die ggfs. in Frage kommenden anderen Anforderungen anderer User berücksichtigt) als dass Microsoft das von Haus aus ins .NET Framework einbauen müsste/sollte.

    Aber da das Framework ja nun Open Source ist, wäre es für dich und andere ja auch möglich, die Idee und den Code dazu beizusteuern. Wer weiß, evtl. findet diese Erweiterung wirklich ihren Weg in die Standardbibliotheken.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Mittwoch, 7. Januar 2015 16:56
    Moderator
  • Hallo,

    Du solltest noch mal darüber nachdenken...

    Die Aufgabe eines Serialisierers ist weder das Filtern, Sortieren oder Verzieren mit Schleifchen... all das widerspricht den SOLID Prinzipien (SRP uam.)

    Hier wäre es sinnvoller Deine Auflistungen, um Methoden zu erweitern, die eine Filterung (sei es fürs Serialisieren oder anderen Gründen) vornehmen. Die könnten z. B. ein IEnumerable<ListType> liefern.

    Auf Instanz-Ebene kann wiederum eine Schnittstelle zur Verallgemeinerung dienen - siehe u. a. Stefans Antwort.

    So bindest Du das Ganze nicht einen spezifischen Serialisierer - ein Tausch ist leichter möglich. Auch eine Nutzung für vergleichbare Aufgaben über die Persistenz hinaus wird dadurch möglich.

    Gruß Elmar

    Mittwoch, 7. Januar 2015 18:36
    Beantworter
  • Hm, widersprechen dann z.B. XmlIgnore und ShouldSerialize nicht diesem Prinzip?

    Nur deshalb kam ja meine Frage eigentlich auf, da es hier ja für Properties Filter-Möglichkeiten gibt.

    Mittwoch, 7. Januar 2015 19:13
  • Hi,

    XmlIgnore eher nicht, da das eine statische Angabe zur Designzeit ist und sich nicht auf einzelne, sondern eben auf alle Instanzen auswirkt. Ob ShouldSerialize den Prinzipien widerspricht, kann ich nicht sagen, könnte aber schon eher sein.

    Mir ist schon klar, dass das, was Du da machen willst, nicht unbedingt ganz abwegig ist und auch ähnlich zu sehen wäre wie ShouldSerialize auf Eigenschaftsebene aber es gibt leider nunmal nichts, was das von dir Gewünschte anbieten würde. Daher wirst Du um eine eigene Logik nicht drumrum kommen.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Mittwoch, 7. Januar 2015 19:18
    Moderator
  • Hallo,

    stell Dir einfach mal vor, morgen wäre die Anforderung die Daten im JSON Format zu serialisieren.

    Welche Lösung passt für beides?

    Gruß Elmar

    Mittwoch, 7. Januar 2015 19:21
    Beantworter
  • Und was würde ich dann mit meinen XmlIgnore und ShouldSerialize() machen? Die müsste ich dann auch wegschmeißen, da der JSON Serializer wieder was anderes erwartet.

    Mir ist eure Aussage schon klar und ich bedanke mich für die Bestätigung, dass es hier nichts Fertiges gibt.


    Mittwoch, 7. Januar 2015 20:09
  • Hallo,

    Bei JSON verwendet man ein anderes Attribut (ScriptIgnore) - und es wäre insofern eine Fleiß-Aufgabe (bzw. Suchen/Ersetzen...). Entsprechendes wäre bei WCF erforderlich - dort DataMember (umgekehrt).

    An der übrigen Logik würde sich nichts ändern, weil man jeweils eine neue Auflistung erzeugt - die das (fiktive) ShouldSerialize berücksichtigt.

    Andersherum, es in jede Art von Serialisierer zu implementieren kostet reichlich Zeit (und Nerven) - und schließt die Nutzung von Third Party Code weitgehend aus.

    Gruß Elmar

    Mittwoch, 7. Januar 2015 20:36
    Beantworter
  • Genau, da stimme ich dir voll zu mit den Zeit und Nerven :)

    Wobei mir der XmlSerializer in .NET bisher sehr gut gefällt und mir wenig Probleme bereitet. Da gab es schon andere Kandidaten...

    Ich sehe kein Hindernis, das es ein ShouldSerialize() (ohne Property-Name) für die gesamte Instanz des Objektes geben könnte. Wenn man bedenkt, dass es das bereits für einzelne Properties gibt.

    Allerdings wenn man es richtig machen will, gebe ich dir natürlich recht. Da sollte man eventuell auch ShouldSerialize() für die Properties vermeiden, denke ich.

    Aber gut, gibt es nicht und damit Schluss :)

    Donnerstag, 8. Januar 2015 07:07
  • Hallo da_rinkes,

    Wenn ein oder mehrere Beiträge Dir weitergeholfen, Aufschlüsse gegeben und Deine Frage beantwortet haben, markiere sie bitte als Antwort.

    Gruß,
    Dimitar


    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „IT-Pros helfen IT-Pros“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.


    Montag, 12. Januar 2015 11:03
    Moderator