AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Programmieren allgemein Klassen dynamisch mit xsd verändern?
Thema durchsuchen
Ansicht
Themen-Optionen

Klassen dynamisch mit xsd verändern?

Ein Thema von jfheins · begonnen am 2. Dez 2009 · letzter Beitrag vom 15. Dez 2009
Antwort Antwort
Benutzerbild von jfheins
jfheins

Registriert seit: 10. Jun 2004
Ort: Garching (TUM)
4.579 Beiträge
 
#1

Klassen dynamisch mit xsd verändern?

  Alt 2. Dez 2009, 14:19
Hallo,

Ich habe gerade eine tolle Aufgabe bekommen

Und zwar gibt es eine datenstruktur (der Einfachheit halber sagen wir mal: eine Liste) in der strukturierte Daten drin sind:
XML-Code:
<liste>
<item>
  <id>8</id>
  <bla>Hallo</bla>
</item>
<item>
  <id>9</id>
  <bla>Welt</bla>
</item>
</liste>
Das lese ich Im Moment einfach in eine List<MyDataClass> ein.
Dazu gibt es (seit heute) auch eine xsd, die diese Struktur beschreibt

Soweit, so gut. Jetzt soll es aber möglich sein, dass der Benutzer (ohne Quellcode) die xsd verändert und bspw. eine color-Eigenschaft vom Typ int mit Wertebereich 0-255 hinzufügt. Das soll dann quasi vollautomatisch übernommen werden.

XML-Code:
<liste>
<item>
  <id>8</id>
  <bla>Hallo</bla>
  <color>243</color>
</item>
<item>
  <id>9</id>
  <bla>Welt</bla>
  <color>145</color>
</item>
</liste>
Jetzt ist natürlich die Klasse "MyDataClass" zur Laufzeit unveränderlich. Erste Lösung wäre also sowas wir ein Dictionary<string, object> wo man dann alle Elemente reinhauen kann. Aber dagegen spricht, dass dann die Typen, die in der xsd ja festgelegt wurden nicht berücksichtigt werden und außerdem könnte man ja auf die Idee kommen, kein einfaches Element zu nehmen sondern in diesem Element noch zu verschachteln.

Deshalb die Frage: Wie würdet ihr sowas angehen?
Btw.: Ich verwende C# (.net 3.5)
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#2

Re: Klassen dynamisch mit xsd verändern?

  Alt 2. Dez 2009, 14:29
Wenn es derart generisch sein soll, würde sich fast schon eine Implementierung von etwas lohnen, was einem Variant ähnelt, der zusätzlich sich selbst beinhalten kann. Ich bin in .NET 3.5 allerdings nicht fit genug um sagen zu können ob es da nicht sogar schon etwas in der Art gibt - XML ist ja "hypig" genug um drauf hoffen zu können dass ein Framework da was bietet .
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Benutzerbild von jfheins
jfheins

Registriert seit: 10. Jun 2004
Ort: Garching (TUM)
4.579 Beiträge
 
#3

Re: Klassen dynamisch mit xsd verändern?

  Alt 2. Dez 2009, 14:38
Damit wäre aber jede Art der Typsicherheit dahin ...

Der Clou soll ja sein, dass man im xsd definiert "ganzzahl zwischen 0 und 255" und dann im Programm beim bearbeiten (benutze dafür im Moment n PropertyGrid) die Eigenschaft "Color" sieht, und auch einen Fehler bekommt wenn man da keine Zahl eintippt

und nein, ich habe keine besondere Lust darauf, einen Variant nachzuprogrammieren. Vorher versuche ich lieber, ein XElement-Objekt mit xsd im Hintergrund zu halten ...
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#4

Re: Klassen dynamisch mit xsd verändern?

  Alt 2. Dez 2009, 14:49
Das Problem ist ja nicht, wie man die Daten im Speicher hält (Ne einfache rekursive Baumstruktur reicht ja und ist einsfixdrei hingebapselt), sondern wie man mit Daten arbeitet, deren Struktur man nicht kennt.

Welchen Wert hat die hinzugefügte Eigenschaft 'Color'? Wo soll sie auftauchen, wie mit ihr 'gerechnet' (im weitesten Sinne) werden? Soll da etwa nur etwas eingegeben werden?

Wenn der Anwender gerade mal eine Spalte zu einer Tabelle hinzufügen kann, geht das ja noch, aber was ist, wenn er auch ganze Unterstrukturen hinzufügen darf? Wie willst Du das dann darstellen und in der Darstellung handhaben?

XSD / XML bietet dir ja diese Möglichkeiten, und um die so gestellte Aufgabe korrekt umzusetzen, kannst du schon mal fast komplett auf klassische Dialoge mit Eingabefeldern und Tabellen / Grids zur Darstellung verzichten. Du kannst also nur mit Baumstrukturen arbeiten, denn nur die bilden eine XML-Struktur korrekt ab, wobei Attribute wieder anders behandelt werden müssten. Nur bei den Attributen, die in ihrer Struktur nicht hierarchisch sind, kannst du flache Strukturen (Dialoge, Grids) verwenden

Machbar ist alles, aber die Vorgabe scheint mir von jemandem zu stammen, der eigentlich keine Ahnung hat.

Ich würde mal bei Altova schauen, wie die soetwas umgesetzt haben und mich eventuell da bedienen.

Wenn es wirklich in dieser Mächtigkeit umgesetzt werden soll, dann würde ich eine Standardlösung vorschlagen (Altova macht da ganz gute Sachen).
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
Benutzerbild von Neutral General
Neutral General

Registriert seit: 16. Jan 2004
Ort: Bendorf
5.219 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#5

Re: Klassen dynamisch mit xsd verändern?

  Alt 2. Dez 2009, 14:57
Hi,

Vielleicht brauchst du ja (soetwas wie) TField..

Delphi-Quellcode:
var f: TField;
begin
  f.DataType := ftInteger;
  f.Size := 255;
end;
Oh es geht ja um C#... Aber da wirds ja sicher auch sowas geben..

?
Michael
"Programmers talk about software development on weekends, vacations, and over meals not because they lack imagination,
but because their imagination reveals worlds that others cannot see."
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#6

Re: Klassen dynamisch mit xsd verändern?

  Alt 2. Dez 2009, 15:07
Wenn das XSD den Typ definiert ist's doch nur halb so wild. Deine Knoten müssen ja nur prinzipiell jeden Typ halten können, und dies nicht implizit bei einer Zuweisung anpassen. Dann wird der Knoten-Sub-Typ eben ausm XSD anhand der ID gesetzt, und der Wert entsprechend zugewiesen. Passen die Typen nicht, gibt's ne Exception. Dass das keine Freude macht denk ich mir

Eine andere Variante (hübscher denk ich), ist eine Knoten-Basisklasse die lediglich definiert dass sie einen Eltern- und N Kindknoten haben kann. Davon dann je Typ eine Klasse ableiten, den Baum anhand der ID und dem XSD aufbauen, und mit den Werten befüllen. Wenn man noch eine Priese Generics einstreut, kann man sich denke ich auch die Ableiterei sparen. Also so ca.:

Code:
class MyNode<T>
{
  public MyNode Parent;
  public List<MyNode> Children;
  public T Data;
}
.
.
.
  MyNode<TypAusXSD> nd = new MyNode<TypAusXSD>;
  nd.Data = DatenAusXML; // Excecption wenn der Typ nicht passt frei Haus
Ich weiss nur grad nicht, ob man in der Klasse MyNode ohne Typfestlegung in der Liste so deklarieren kann
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Benutzerbild von jfheins
jfheins

Registriert seit: 10. Jun 2004
Ort: Garching (TUM)
4.579 Beiträge
 
#7

Re: Klassen dynamisch mit xsd verändern?

  Alt 2. Dez 2009, 15:20
Danke für die Antworten

Eine gute Nachricht - ich habe kurz gemeckert und das mit den komplexen verschachtelten Typen ist erstmal vom Tisch

Ich mache dann erstmal so ne Art Tabelle mit 3 Spalten "Name", "Typ", "Inhalt" - die ersten 2 Spalten lese ich dann aus der xsd, die letzte wird dann mit den Daten befüllt ...
(verschachtelte Sachen kommen dann mit "ComplexType" und ihrem XML-String da rein und sind nicht editierbar)
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#8

Re: Klassen dynamisch mit xsd verändern?

  Alt 2. Dez 2009, 15:24
Wieso nimmst Du nicht gleich ein XmlDocument? Das liest ein, strukturiert, validiert etc. Alles, was du brauchst.
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
Benutzerbild von jfheins
jfheins

Registriert seit: 10. Jun 2004
Ort: Garching (TUM)
4.579 Beiträge
 
#9

Re: Klassen dynamisch mit xsd verändern?

  Alt 2. Dez 2009, 15:29
Im Moment verwende ich XDocument (Namespace System.Xml.Linq) - aber ich werde mir das direkt mal anschauen, danke

Hmmm ... sieht so aus als wären das einfach nur 2 verschiedene Arten, ein XML-Dokument zu bearbeiten - wobei mir die LINQ-Variante mehr zusagt
  Mit Zitat antworten Zitat
Benutzerbild von jfheins
jfheins

Registriert seit: 10. Jun 2004
Ort: Garching (TUM)
4.579 Beiträge
 
#10

Re: Klassen dynamisch mit xsd verändern?

  Alt 15. Dez 2009, 11:43
So, falls das nochmal wen interessiert, hier meine Lösung:

Der Graph (Containerklasse für Kanten und Knoten) implementiert das Interface ICustomTypeDescriptor - damit kann man dynamisch Zeilen im PropertyGrid hinzufügen.

Ein Feld in der Klasse dient als Sammelbecken für alle nicht-erkannten Attribute:
Code:
        private List<XAttribute> Attrib;
Dann muss man mindestens eine Methode wie folgt überschreiben:
Code:
        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            var normal = TypeDescriptor.GetProperties(this, attributes, true);

            var all = new List<PropertyDescriptor>(normal.Count + Attrib.Count);

            foreach (PropertyDescriptor item in normal)
            {
                all.Add(item);
            }

            foreach (var item in Attrib)
            {
                if (VariantDescriptor.Supports(item))
                {
                    all.Add(new VariantDescriptor(item));
                }
            }
           
            return new PropertyDescriptorCollection(all.ToArray());
        }
Den Rest kann man einfach weiterleiten:
Code:
        public string GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }
        public string GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this, true);
        }
        public TypeConverter GetConverter()
        {
            return TypeDescriptor.GetConverter(this, true);
        }
/// etc...
Und hier kommt der Interessante Teil: Der PropertyDescriptor der das typsichere Bearbeiten ermöglicht:
Code:
    /*  Diese Klasse leistet die Darstellung der verschidenen XAttribute
     *  im PropertyGrid. Es werden einfache Typen unterschieden, und zum
     *  Editieren in ihre C#-Equivalente überführt.
     *  Getestet mit int, float, double, decimal, string und DateTime, ggf. switches erweitern.
     *  Unbekannte Typen sollen nicht editierbar gemacht werden,
     *  wenn TypeCodeToType() den Wert null zurückgibt. (kann mit Supports() geprüft werden)
     * 
     *  Klasse benötigt Schema-Infos.
     * 
     */
    class VariantDescriptor : PropertyDescriptor
    {
        private XAttribute attr;
        private XmlTypeCode type;

        public VariantDescriptor(XAttribute attribute)
            : base(attribute.Name.ToString(), null)
        {
            attr = attribute;
            var info = attr.GetSchemaInfo();

            if (info == null)
                throw new ArgumentException("Schema-Info is required!\r\nUse XDocument.Validate(..., ..., true) or equivalent.");
           
            type = attr.GetSchemaInfo().SchemaType.TypeCode;
           
            if (TypeCodeToType(type) == null)
                throw new NotImplementedException(string.Format("This type (\"{0}\") is not supported.", type.ToString()));
        }

        public override bool CanResetValue(object component)
        {
            return false;
        }

        public override Type ComponentType
        {
            get { return typeof(Graph); }
        }

        public override object GetValue(object component)
        {
            switch (type)
            {
                case XmlTypeCode.Time:
                case XmlTypeCode.Date:
                case XmlTypeCode.DateTime:
                    return XmlConvert.ToDateTime(attr.Value, XmlDateTimeSerializationMode.RoundtripKind);

                // Konvertierung nötig, um das Dezimaltrennzeichen korrekt zu wandeln
                case XmlTypeCode.Decimal:
                    return XmlConvert.ToDecimal(attr.Value);
                case XmlTypeCode.Double:
                    return XmlConvert.ToDouble(attr.Value);
                case XmlTypeCode.Float:
                    return XmlConvert.ToSingle(attr.Value);

                default:
                    return attr.Value;
            }
        }

        public override bool IsReadOnly
        {
            get { return false; }
        }

        public override Type PropertyType
        {
            get
            {
                return TypeCodeToType(type);
            }
        }

        private static Type TypeCodeToType(XmlTypeCode TypeCode)
        {
            switch (TypeCode)
            {
                case XmlTypeCode.Boolean:
                    return typeof(Boolean);
                case XmlTypeCode.UnsignedByte:
                case XmlTypeCode.Byte:
                    return typeof(byte);
                case XmlTypeCode.Date:
                case XmlTypeCode.Time:
                case XmlTypeCode.DateTime:
                    return typeof(DateTime);
                case XmlTypeCode.Decimal:
                    return typeof(decimal);
                case XmlTypeCode.Double:
                    return typeof(double);
                case XmlTypeCode.Float:
                    return typeof(float);
                case XmlTypeCode.Int:
                    return typeof(int);
                case XmlTypeCode.NonPositiveInteger:
                case XmlTypeCode.NegativeInteger:
                case XmlTypeCode.Integer:
                case XmlTypeCode.Long:
                    return typeof(long);
                case XmlTypeCode.Short:
                    return typeof(short);
                case XmlTypeCode.Name:
                case XmlTypeCode.NCName:
                case XmlTypeCode.NmToken:
                case XmlTypeCode.Text:
                case XmlTypeCode.String:
                    return typeof(string);
                case XmlTypeCode.UnsignedInt:
                    return typeof(uint);
                case XmlTypeCode.NonNegativeInteger:
                case XmlTypeCode.PositiveInteger:
                case XmlTypeCode.UnsignedLong:
                    return typeof(ulong);
                case XmlTypeCode.UnsignedShort:
                    return typeof(ushort);
                default:
                    return null;
            }
        }

        public override void ResetValue(object component)
        {
        }

        public override void SetValue(object component, object value)
        {
            switch (type)
            {
                case XmlTypeCode.Time:
                case XmlTypeCode.Date:
                case XmlTypeCode.DateTime:
                    attr.Value = XmlConvert.ToString((DateTime)value, XmlDateTimeSerializationMode.RoundtripKind);
                    break;

                // Konvertierung nötig, um das Dezimaltrennzeichen korrekt zu wandeln
                case XmlTypeCode.Decimal:
                    attr.Value = XmlConvert.ToString((decimal)value);
                    break;
                case XmlTypeCode.Double:
                    attr.Value = XmlConvert.ToString((double)value);
                    break;
                case XmlTypeCode.Float:
                    attr.Value = XmlConvert.ToString((float)value);
                    break;

                default:
                    attr.Value = value.ToString();
                    break;
            }
        }

        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }

        internal static bool Supports(XAttribute item)
        {
            return TypeCodeToType(item.GetSchemaInfo().SchemaType.TypeCode) != null;
        }
    }
Wenn jetzt ein objekt der Klasse graph einem properyGrid zugewiesen wird, erscheinen die zusätzlichen Attribute wie normale Eigenschaften und sind ebenso editierbar. Typsicherheit ist gegeben (z.B. "100000" ist kein gültiger wert für System.Short)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:36 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz