Autor Beitrag
Namenlosnameless
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 259
Erhaltene Danke: 6

Windows XP Home Edition, Windos Vista
C#
BeitragVerfasst: Do 08.08.13 14:02 
Hallo!

Ich stehe seit einiger Zeit bei einem Problem an...

Ich habe eine Struktur, die wie folgt aussieht:
Eine ObservableCollection<GroupGuideModel> soll mittels XML gespeichert werden.

Wobei GroupGuideModel von einer ObservableCollection<SubGuideModel> erbt. SubGuideModel wiederum erbt von ObservableCollection<GuideModel>

GroupGuideModel präsentiert die Property Title
SubGuideModel präsentiert die Propertys Title, Image, Author
GuideModel präsentiert weit über 25 Propertys..


Schicke ich nun die ObservableCollection<GroupGuideModel> Model in die Serialisierung
mit:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
using (Stream stream = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync("GroupGuideModels.mfg", CreationCollisionOption.ReplaceExisting))
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<GroupGuideModels>));
                    serializer.Serialize(stream,Model);
                }


Bekomme ich einen XML-Code der die Propertys von GroupeGuideModel und SubGuideModel komplett ignoriert, dafür aber die Propertys vom GuideModel korrekt speichert...
ausblenden XML-Daten
1:
2:
3:
4:
<ArrayOfArrayOfArrayOfGuideModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ArrayOfArrayOfGuideModel>
    <ArrayOfGuideModel>
      <GuideModel>


Ich hab schon den ganzen gestrigen Tag vor Google verbracht und die einen sagen man darf nicht "Downcasten" sondern muss "Upcasten" also müsste ich eine List<GuideModel> speichern. Das bringt nur leider nicht den gewünschten Effekt

Dann bin ich auf das [XmlInclude ()] gestoßen... Für die Klasse GroupeGuideModel hab ich folgendes gesetzt:
ausblenden C#-Quelltext
1:
2:
3:
4:
[XmlInclude (typeof(SubGuideModel))]
   [XmlInclude (typeof(GuideModel))]
   public class GroupGuideModels:ObservableCollection<SubGuideModel>
    {

Bei SubGuideModel ist entsprechend nur das GuideModel inkludiert...

Leider war auch dies erfolglos...

Ich hoffe ihr könnt mir da weiterhelfen, sonst schreib ich die Speicherfunktion einfach von Hand und häng alles über Indices zusammen!


mfg Namenlosnameless

_________________
1:<<Life sucks!!>> 2:<< Well okay>> 1: <<Just Yours>> 2:<<Ohmph>>
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 08.08.13 15:38 
Ich würde versuchen das über XMLArrayItemAttribute zu lösen. Da bist du auch gleich weg von der dämlichen ArrayOf Benamsung.

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
public class Root  
{
    public Root()
    {
        Model = new ObservableCollection<Base>();
    }
    [XmlArray("Model")]
    [XmlArrayItem("Base"typeof(Base))]
    [XmlArrayItem("Derived"typeof(Derived))]
    public ObservableCollection<Base> Model { get; private set; }
}

public class Base
{
    public string BaseProperty { get; set; }
}

public class Derived : Base
{
    public string DerivedProperty { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Root root = new Root();
        root.Model.Add(new Base() { BaseProperty = "Base" });
        root.Model.Add(new Derived() { BaseProperty = "Base", DerivedProperty = "Derived" });

        XmlSerializer serializer = new XmlSerializer(typeof(Root));
        using(StringWriter writer = new StringWriter())
        {
            serializer.Serialize(writer, root);
            Console.WriteLine(writer.ToString());
        }
        Console.ReadLine();
    }
}


Ergebnis

ausblenden XML-Daten
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
<?xml version="1.0" encoding="utf-16"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Model>
    <Base>
      <BaseProperty>Base</BaseProperty>
    </Base>
    <Derived>
      <BaseProperty>Base</BaseProperty>
      <DerivedProperty>Derived</DerivedProperty>
    </Derived>
  </Model>
</Root>



Edit: Falls dein Problem ist, oder auch ist, das Properties deiner ObservableCollection Ableitungen nicht geschrieben werden. Dann ist das ein grundsätzliches Problem. Jede IEnumerable Ableitung serialisiert nur seine enthaltenen Elemente aber nicht sich selbst. Wen du das brauchst solltest du die Listen umdesignen von Ableitung auf eine Property die die Liste darstellt. Der Klasse wiederum kann man dann ja einen angepassten Enumerator verpassen wenn es einen stört das man nicht mehr direkt über die Klasse iterieren kann.
Namenlosnameless Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 259
Erhaltene Danke: 6

Windows XP Home Edition, Windos Vista
C#
BeitragVerfasst: Do 08.08.13 18:54 
Danke für die schnelle und sehr hilfreiche Antwort, das hat mich der Lösung um einiges näher gebracht, aber leider noch nicht ganz...

Ich habe jetzt 2 Dinge versucht:
1) Die Klasse die die Liste Enthält zu speichern...
Allerding bekomme ich eine FileNotFoundException vom Serializer ausgeworfen
ausblenden Quelltext
1:
    Message  "Die Datei oder Assembly \"WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" oder eine Abhängigkeit davon wurde nicht gefunden. Das System kann die angegebene Datei nicht finden."  string					


2) Wenn ich nur die Liste Serialisieren will, ändern die XML-Tags nichts am Aufbau des Files...


Mir is auch in prinzipieller Unterschied zu deinem Beispiel aufgefallen: Bei dir erbt die Klasse Derived direkt von Base! Bei mir erben die Klassen aber von einer IEnumerable der jeweilig tiefergestellten Klasse...

Also GroupGuideModel is der Überbegriff erbt aber von einer Liste des Unterbegriffs. Ich kenn mich mit Serialisierung viel zu wenig aus, um ahnen zu können ob da der Fehler liegt.
Weiters liegt die Klasse die das Speichern übernimmt in einem anderen Namespace als die Klasse die gespeichert werden soll... auch hier wieder nur eine Idee wo der Fehler liegen könnte...

mfg Namenlosnameless

_________________
1:<<Life sucks!!>> 2:<< Well okay>> 1: <<Just Yours>> 2:<<Ohmph>>
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 08.08.13 20:36 
Zitat:
Allerding bekomme ich eine FileNotFoundException vom Serializer ausgeworfen


Klingt nach Versionsmischmasch in deiner Anwendung. Vom Framework 3.5 nach 4 ist die ObservableCollection aus WindowsBase nach System gewandert.
Benutzt du was kleiner Framework 4 oder 4 bzw. größer? Wenn du 4 oder größer verwendest ist dir vielleicht zufällig irgendwas älteres dazwischen gerutscht?

Zitat:
Bei mir erben die Klassen aber von einer IEnumerable der jeweilig tiefergestellten Klasse...


Das solltest du eher nicht tun solange du denn XMLSerializer benutzt. Wie gesagt hat der XMLSerializer eine Sonderbehandlung für IEnumerable Klassen. Sobald diese Klassen mehr machen als Listenelemente zu verwalten ist der XMLSerializer dafür blind. Du könntest alternativ mal den DataContractSerializer versuchen. Der ist an dieser Stelle etwas verzeihlicher hat aber natürlich andere Probleme ;)
Namenlosnameless Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 259
Erhaltene Danke: 6

Windows XP Home Edition, Windos Vista
C#
BeitragVerfasst: Do 08.08.13 20:58 
Also Grundsätzlich schreib ich alles mit VS2012 für Windows8-Store-Apps also .NET 4.5
Die ObservableCollection liegt im using System.Collections.ObjectModel; und das in allen beteiligten Klassen...

_________________
1:<<Life sucks!!>> 2:<< Well okay>> 1: <<Just Yours>> 2:<<Ohmph>>
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 08.08.13 21:16 
Zitat:
Die ObservableCollection liegt im using System.Collections.ObjectModel; und das in allen beteiligten Klassen...


Und in dem Namespace war die auch schon immer. Wenn die die bei Microsoft anfangen würden Klassen in den Namespaces zu verschieben dann hätten wir wirkliche Probleme.
Aber die haben die Klasse in eine andere Assembly verschoben. Es geht also darum was du referenzierst nicht welche Namespace Shortcuts du in deinen Dateien eingefügt hast.

Ich habe mir mal die Definition von ObservableCollection in einem Disassembler angeschaut da hängt ein TypeForwardedFrom Attribut dran mit Bezug auf die WindowsBase Assembly.
Das scheint mir sehr merkwürdig aus einer 4er Assembly eine 3er Assembly zu referenzieren. Ich versuch gerade mal rauszufinden ob das irgendeinen Einfluß auf die Serialisierung hat.

ausblenden C#-Quelltext
1:
2:
[Serializable, TypeForwardedFrom("WindowsBase, Version=3.0.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
Namenlosnameless Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 259
Erhaltene Danke: 6

Windows XP Home Edition, Windos Vista
C#
BeitragVerfasst: Do 08.08.13 21:32 
Danke!

Ich hab mir jetzt die Metadaten von der ObservableCollection angeschaut die ich verwende:
Assembly System.ObjectModel.dll, v4.0.0.0... also vlt doch .NET 4.0?

_________________
1:<<Life sucks!!>> 2:<< Well okay>> 1: <<Just Yours>> 2:<<Ohmph>>
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 08.08.13 21:40 
Zitat:
Assembly System.ObjectModel.dll, v4.0.0.0... also vlt doch .NET 4.0?


Nein. Das heißt es ist WinRT. Drecks Framework Chaos.

Ich würde empfehlen einfach mal die WindowsBase Assembly auch zu referenzieren. Vielleicht ist das bei WinRT nötig.
Namenlosnameless Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 259
Erhaltene Danke: 6

Windows XP Home Edition, Windos Vista
C#
BeitragVerfasst: Do 08.08.13 22:34 
Wie füge ich WindowsBase 3.0 ein? Einfach using Windows;??
Tut mir leid wenn ich jetz anfange CaptainObvious-Fragen zu stellen, aber da ich absoluter autoditakt in sachen Programmierung bin, ist mein Wissen über die .NET-Architektur sehr eingeschränkt...

Wenn ich unter Referenzen auf die Assemblys gehe steht dort dass bereits alle verfügbaren eingebunden sind...

Wenn man im Internet wegen der FileNotFoundException googlet, bekommt man einen Haufen antworten, dass es ganz normal ist, und man das ignorieren kann... leider kann ich das nicht weil das geschriebene File leer ist!

_________________
1:<<Life sucks!!>> 2:<< Well okay>> 1: <<Just Yours>> 2:<<Ohmph>>
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 08.08.13 23:40 
Unter Referenzen zu schauen war schon richtig. Wenn da WindowsBase nicht auswählbar ist weiß ich auch nicht wie und warum. Hab gerade kein WinRT zur Hand um nachzusehen.

Zitat:
Wenn man im Internet wegen der FileNotFoundException googlet, bekommt man einen Haufen antworten, dass es ganz normal ist, und man das ignorieren kann... leider kann ich das nicht weil das geschriebene File leer ist!


Das ist möglich. Beim Serialisieren fliegen reichlich stille Exceptions im Framework Code. Die solltest du aber nur im Debugger zu sehen bekommen wenn du an den Debugger Settings rumgespielt hast. Z.b. Wenn du unter Debug/Exceptions den Debugger angewiesen hast beim werfen einer Exception anzuhalten und nicht nur wenn sie behandelt wird.

Um sowas auszuschließen kannst du aber auch einfach mal deinen Serialisierungscode in einen Exception Block packen. Wenn du dort dann nicht im catch landest waren es stille Exceptions die du tatsächlich ignorieren kannst.

Kannst du deinen Code mal soweit auf das wesentliche reduzieren um uns diesen Teil hier mal kompilierbar zu präsentieren? Ich habe deine Struktur nur teilweise verstanden. Vieleicht erkenne ich oder jemand anderes dann das Problem.
Namenlosnameless Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 259
Erhaltene Danke: 6

Windows XP Home Edition, Windos Vista
C#
BeitragVerfasst: Fr 09.08.13 00:17 
Also:

Die Klasse die sich um das Speichern kümmern sollte:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
using Moba_Guides.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Windows.Storage;

namespace Moba_Guides.FileSystem
{
    class SaveClass
    {
        public AppViewModel avm { get; set; } //Das AppViewModel = DataContext von der Hauptseite ==> Soll serialisiert werden!
        
        public async void SerializeXML()
        {
            
                using (Stream stream = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync("AppViewModel.mfg", CreationCollisionOption.ReplaceExisting)) //Die Zeile müsste man auf was nicht WinRT spezifisches umändern!
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(AppViewModel));
                    serializer.Serialize(stream,avm);
                }
        }
    }
}


Die AppViewModel Klasse:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
using System;
using Windows;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace Moba_Guides.Model
{
    public class AppViewModel
    {
        [XmlArray ("Model")]
        [XmlArrayItem ("ggm",typeof(GroupGuideModels))]
        [XmlArrayItem ("sgm",typeof(SubGuideModel))]
        [XmlArrayItem ("gm",typeof(GuideModel))]
        public ObservableCollection<GroupGuideModels> Items { get; set; }
        [XmlIgnore]
        public ObservableCollection<GuideModel> Builds { get; set; }

        public AppViewModel()
        {

//Befüllung mit DummyDaten
        }


    }
}


Die GroupGuideModel-Klasse (hirarchisch ganz oben):

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Windows;

namespace Moba_Guides.Model
{
   
   public class GroupGuideModels:ObservableCollection<SubGuideModel>
    {
        public string Title { get; set; }
       
    }
}


Die SubGuideModel-Klasse (hirarchische Mitte):
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Windows;
namespace Moba_Guides.Model
{
    
    public class SubGuideModel:ObservableCollection<GuideModel>
    {
        [XmlAttribute ("Title")]
        public string Title { get; set; }
        [XmlAttribute("Image")]
        public string Image { get; set; }
        [XmlAttribute("Author")]
        public string Author { get; set; }
    }
   
}


Und zuletzt, die GuideModelKlasse (hirarisch ganz unten):
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
public class GuideModel
    {
        
        public string Lane { get; set; }
        public string Title { get; set; }
        public string Image { get; set; }
//Auszug der Propertys
       
        public GuideModel()
        {
            // Fill DummyDaten
        }
       
    }
}

_________________
1:<<Life sucks!!>> 2:<< Well okay>> 1: <<Just Yours>> 2:<<Ohmph>>
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Fr 09.08.13 10:42 
Zitat:
[XmlArrayItem ("ggm",typeof(GroupGuideModels))]
[XmlArrayItem ("sgm",typeof(SubGuideModel))]
[XmlArrayItem ("gm",typeof(GuideModel))]
public ObservableCollection<GroupGuideModels> Items { get; set; }


GuideModel ist kein GroupGuideModels.In der ObservableCollection kann nie ein GuideModel stecken genausowenig ein SubGuideModel. Darüber sollte der Serializer schon stolpern.

Zitat:
public class GroupGuideModels : ObservableCollection<SubGuideModel>
{
public string Title { get; set; }
}


Titel würde nie Serialisiert werden. Listen(IEnumerable bzw. ICollection Ableitungen) serialisieren nur ihre Items nicht aber ihre eigene Oberfläche(Siehe Hinweise in der msdn Doku)


Die ObservableCollectiosn solltest du als Member der Klassen darstellen und nicht über Ableitung.
Namenlosnameless Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 259
Erhaltene Danke: 6

Windows XP Home Edition, Windos Vista
C#
BeitragVerfasst: Fr 09.08.13 14:16 
Da steckt auch schon das Problem, soweit ich mich mit WinRT und dem GridView im MVVM auskenne, brauche ich die abgeleiteten Klassen um die doppelt geordnete Liste (GroupGuideModel > SubGuideModel) im GridView anzuzeigen...

Ich setzte die CollectionViewSource auf eine Liste von GroupGuideModel und XAML erkennt automatisch die IEnumerable, die mit der Klasse geerbt wird, und kann so die Liste erstellen (glaub ich zumindest so verstanden zu haben)
Wenn es einen anderen Weg, der mir die ObservableCollection als Property zur Verfügung stellt, wär ich sehr dankbar ;)

_________________
1:<<Life sucks!!>> 2:<< Well okay>> 1: <<Just Yours>> 2:<<Ohmph>>
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Fr 09.08.13 15:03 
Da kenne ich mich in WPF nicht gut genug aus. Aus Leihensicht finde ich das aber merkwürdig. Gebunden wird doch das ViewModel serialisiert eher das Model. Dein Model soll aber beides machen?
Namenlosnameless Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 259
Erhaltene Danke: 6

Windows XP Home Edition, Windos Vista
C#
BeitragVerfasst: Fr 09.08.13 17:38 
naja der DataContext ist die Klasse AppViewModel die enthält unteranderem die Liste mit allen GroupGuideModels

Den Name "Models" haben die Klasse nur weil sie der DataContext von den jeweiligen DetailPages sind...

_________________
1:<<Life sucks!!>> 2:<< Well okay>> 1: <<Just Yours>> 2:<<Ohmph>>
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Fr 09.08.13 18:07 
Und warum willst du das ViewModel persistieren? Die relevanten Daten stecken doch im Model. Sollte man nicht das Model persistieren?