Laden...

XML Deserialiseren mit Parent Info

Erstellt von Jelly vor 13 Jahren Letzter Beitrag vor 13 Jahren 1.099 Views
J
Jelly Themenstarter:in
1.114 Beiträge seit 2007
vor 13 Jahren
XML Deserialiseren mit Parent Info

Ich habe ein XML File, mit 2-3 Tiefenebenen. Das Deserialisieren mach ich über die Serialzer Klasse. Das Ergebnis sind dann Klassen mit Unterklassen und Unterunterklassen. Das klappt wunderbar, ist ja auch nix weltberaubendes. Alles was ich brauchte ist eben meine Datenklassen so aufzubauen und mit den XmlAttributen zu versehen, dass das Deserialisieren eben passt (ein xsd Schema liegt mir nicht vor).

Ich habe aber nun das Problem, dass ich die Parent Klasse aus meiner Childklasser heraus brauche. XML Strukturen sind ja immer als Baum zu verstehen, d.h. abgesehen von Root Node haben alle Nodes auch immer einem Parent. Und aus dem Parent Node bräuchte ich aus meiner Childklasse eben die Parentinstanz.

Geht das irgendwie mit Bordmittel beim Deserialisieren oder was muss ich da tun? Ein geeignetes XmlAttribut habe ich dazu jedenfalls nicht gefunden.

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

der XmlSerializer unterstützt keine zyklischen Abhängigkeiten. Mit dem DataContractSerializer wäre das möglich. Falls du den verwenden kannst nimm diesen - dort wird in der XML aber mit zusätzlichen IDs für die Referenzen gearbeitet, d.h. wenn das XML vorgegeben ist geht das nicht.

Sonst kannst du selbst mit IXmlSerializable deserilieren und so den Object-Graphen aufbauen, oder du setzt die Parents nach der Deserialisierung mit dem XmlSerializer in einer Schleife selbst.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

J
Jelly Themenstarter:in
1.114 Beiträge seit 2007
vor 13 Jahren

der XmlSerializer unterstützt keine zyklischen Abhängigkeiten. Mit dem DataContractSerializer wäre das möglich. Falls du den verwenden kannst nimm diesen

Den könnte ich schon verwenden. So direkt ist mir da aber auch nicht bekannt, wie ich dort eine Parent Eigenschaft berücksichtigen kann?

Momentan nutze ich deinen letzten Vorschlag, d.h. nach der Deserialisierung iteriere ich durch meine Collections und setze entsprechend die Parent Eigenschaft. Das find ich aber unschön.

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

So direkt ist mir da aber auch nicht bekannt, wie ich dort eine Parent Eigenschaft berücksichtigen kann?

Ganz einfach 😉

Beispiel:


using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Diagnostics;

namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			Parent parent   = new Parent { Name = "Vater" };
			parent.Children = new List<Child>();
			parent.Children.Add(new Child { Name = "Kind 1", Parent = parent });
			parent.Children.Add(new Child { Name = "Kind 2", Parent = parent });

			DataContractSerializer ser = new DataContractSerializer(
				typeof(Parent),
				null,
				int.MaxValue,
				true,
				true,	// true für Graphen
				null);

			using (FileStream fs = File.Create("family.xml"))
				ser.WriteObject(fs, parent);

			parent = null;
			using (FileStream fs = File.OpenRead("family.xml"))
				parent = ser.ReadObject(fs) as Parent;

			Debug.Assert(parent.Children.Count == 2);
		}
	}
	//-------------------------------------------------------------------------
	public class Parent
	{
		public string Name          { get; set; }
		public List<Child> Children { get; set; }
	}
	//-------------------------------------------------------------------------
	public class Child
	{
		public Parent Parent { get; set; }
		public string Name   { get; set; }
	}
}

ergibt folgendes XML:


<Parent z:Id="1" 
		xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1" 
		xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
		xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
	<Children z:Id="2" z:Size="2">
		<Child z:Id="3">
			<Name z:Id="4">Kind 1</Name>
			<Parent z:Ref="1" i:nil="true"/>
		</Child>
		<Child z:Id="5">
			<Name z:Id="6">Kind 2</Name>
			<Parent z:Ref="1" i:nil="true"/>
		</Child>
	</Children>
	<Name z:Id="7">Vater</Name>
</Parent>

Dort siehst du auch dass die Referenzen mit z:Id und z:Ref angegeben werden, aber im Code ist das alles ganz transparent.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

J
Jelly Themenstarter:in
1.114 Beiträge seit 2007
vor 13 Jahren

Hallo gfoidl,

danke für den Codeschnippsel. Mir waren die Parameter im DataContractSerializer nicht alle bekannt. So scheint es also zu klappen.

Aber ich frage mich eigentlich, wozu diese Id's im XML benötigt werden? Eigentlich geht doch schon aus der XML Struktur selbst hervor, wer der Parent ist! Ok, du hast die ganzen Child-Nodes nochmals in Children gruppiert, aber der Parent Node ist dem ganzen doch eigentlich übergeordnet.

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

Aber ich frage mich eigentlich, wozu diese Id's im XML benötigt werden?

Für dieses einfache Beispiel macht es keinen Sinn. Schau dir mal das an:


<Father z:Id="1" 
		xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1" 
		xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
		xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
	<Children z:Id="2" z:Size="2">
		<Child z:Id="3">
			<Father z:Ref="1" i:nil="true"/>
			<Mother z:Id="4">
				<IsWife>true</IsWife>
				<Name z:Id="5">Frau</Name>
			</Mother>
			<Name z:Id="6">Kind1</Name>
		</Child>
		<Child z:Id="7">
			<Father z:Ref="1" i:nil="true"/>
			<Mother z:Id="8">
				<IsWife>false</IsWife>
				<Name z:Id="9">Affäre</Name>
			</Mother>
			<Name z:Id="10">Kind2</Name>
		</Child>
	</Children>
	<Name z:Id="11">Vater</Name>
	<Women z:Id="12" z:Size="2">
		<Woman z:Ref="4" i:nil="true"/>
		<Woman z:Ref="8" i:nil="true"/>
	</Women>
</Father>

Ohne Ids bzw. ohne Referenz müssten bestimmte Elemente doppelt (allgemein: mehrfach) geschrieben werden - DRY gilt auch für XML 😉

Zumal zu einem Objekt neben Zustand und Verhalten ja auch die Identität gehört wird diese als Int-Id mitgespeichert. Der BinaryFormatter machts es übrigens auch nicht viel anders. Im obigen Beispiel wäre Id kein Bezug von zB Mutter des Kind2 zur Affäre des Mannes denn es wäre ein neues Objekt. Durch mitserialisieren der Id ist die Zugehörigkeit aber eindeutig.

Das zugehörige Objektmodell:


using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;

namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			Father father = new Father { Name = "Vater" };
			Woman wife    = new Woman  { Name = "Frau",   IsWife = true };
			Woman affair  = new Woman  { Name = "Affäre", IsWife = false };

			Child child1 = new Child
			{
				Name   = "Kind1",
				Father = father,
				Mother = wife
			};

			Child child2 = new Child
			{
				Name   = "Kind2",
				Father = father,
				Mother = affair
			};

			father.Children = new List<Child> { child1, child2 };
			father.Women    = new List<Woman> { wife, affair };

			DataContractSerializer ser = new DataContractSerializer(
				typeof(Father),
				null,
				int.MaxValue,
				true,
				true,    // true für Graphen
				null);

			using (FileStream fs = File.Create("family.xml"))
				ser.WriteObject(fs, father);

			father = null;
			using (FileStream fs = File.OpenRead("family.xml"))
				father = ser.ReadObject(fs) as Father;

			Debug.Assert(father.Children.Count == 2);
		}
	}
	//-------------------------------------------------------------------------
	public class Father
	{
		public string Name          { get; set; }
		public List<Woman> Women    { get; set; }
		public List<Child> Children { get; set; }
	}
	//-------------------------------------------------------------------------
	public class Woman
	{
		public string Name { get; set; }
		public bool IsWife { get; set; }
	}
	//-------------------------------------------------------------------------
	public class Child
	{
		public Father Father { get; set; }
		public Woman Mother  { get; set; }
		public string Name   { get; set; }
	}
}

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

J
Jelly Themenstarter:in
1.114 Beiträge seit 2007
vor 13 Jahren

Yes... Das Beispiel leuchtet ein. Das Modell erlaubt also nicht nur eine n:1 Beziehung zwischen Child und Parent, sondern eine n:m (mehrere Parents). Das kann in gewissen Situationen durchaus gefordert sein.

Danke für den Einblick.