2 Stimmen

Wie kann ein unbekannter Typ aus XML deserialisiert werden?

Betrachten Sie das folgende übermäßig vereinfachte Stück XML:

   5

Insbesondere betrachtend das AttributeValue Element, aus dem DataType Attribut weiß ich, dass mein Wert vom Typ integer ist (obwohl es auch double, string, datetime sein könnte... irgendein etablierter Datentyp aus dem w3 Standard). Ich möchte dieses XML gerne zu einer .NET-Klasse mit dem stark typisierten Wert deserialisieren. Das erste, was mir in den Sinn kam, war, eine generische AttributeValue-Klasse zu erstellen:

public class AttributeValue
{
    public T Value {get; set;}
}

aber natürlich wird das aus ein paar Gründen nicht funktionieren - der größte Grund ist, dass ich den Typ in der Elternklasse deklarieren müsste, was nicht kompiliert, weil T nicht definiert ist:

public class ElementA
{
    public AttributeValue {get; set; }  // Natürlich wird das nicht funktionieren, weil T
}                                          // nicht definiert ist.

Außerdem müsste ich wahrscheinlich IXmlSerializable in meine Klasse implementieren, um die benutzerdefinierte Serialisierung zu behandeln.

Gibt es einen besseren Weg, dieses Problem zu lösen? Ich weiß, dass ich das DataType Attribut in meinem Code serialisieren und den Wert als String speichern kann, um ihn später zu konvertieren, aber es wäre hilfreich, tatsächlich den richtigen Typ in meinem Geschäftsobjekt für spätere Verarbeitung zu haben.

Vielen Dank für jede Hilfe!

Jason

2voto

caesay Punkte 16384

Nun ich weiß, dass dies keine genaue Antwort auf Ihre Frage ist, aber Sie könnten eine Lösung mit Dynamik in .Net 4 implementieren. Hier ist ein Beispiel:

public class DynamicElement : DynamicObject
{
    public Dictionary Attributes
    {
        get { return lst; }
    }

    private Dictionary lst;

    public DynamicElement()
    {
        lst = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
    }

    public bool Present(string name)
    {
        if (lst == null) return false;
        if (!lst.ContainsKey(name)) return false;

        return true;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var name = binder.Name;
        result = null;

        if (lst == null) return false;
        if (!lst.ContainsKey(name)) return false;

        result = lst[name];
        return true;
    }
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        var name = binder.Name;

        if (lst == null) return false;

        if (!lst.ContainsKey(name))
            lst.Add(name, value);
        else
            lst[name] = value;

        return true;
    }
}

Und dann zur Verwendung wäre es ähnlich wie folgt:

dynamic d = new DynamicElement();
d.AttributeValue = Convert.ToInt32(xmlElement.Value);
d.Done = true; //nur ein weiteres Beispiel.

später:

public void something(DynamicElement de)
{
    dynamic d = de;
    if(d.Done) //denken Sie daran, dies oben definiert zu haben.. nur ein Beispiel.
    {
        int someValue = d.AttributeValue;
    }
}

Der Nachteil ist, dass es keine Intellisense geben wird. Alles wird zur Laufzeit aufgelöst. Sie können auch überprüfen, ob ein Wert vorhanden ist mit dem d.Present("AttributeName"); Entschuldigung, wenn es nicht ganz kompiliert. Ich habe es im Notepad geschrieben :)

BEARBEITEN:

Es sollte auch nicht schwer sein, Serialisierung zu implementieren - denn alles, was Sie tun müssen, ist über das Attributswörterbuch zu iterieren.

2voto

Rockdocta Punkte 605

Ich schätze Ihre Antwort @caesay und habe sie umgesetzt, bin mir aber nicht sicher, ob ich diese Art von Funktionalität benötige (mehrere Eigenschaften dem Wörterbuch hinzufügen zu können). Auch wenn ich dynamo dringend in meinem Code verwende, versuche ich, es zu vermeiden, wo es möglich ist.

Stattdessen habe ich die folgende Struktur implementiert, um den generischen Typ innerhalb meiner Elternklasse beizubehalten:

public class AttributeValueElement : XACMLElement
{

    public AttributeValueElement()
        : base(XacmlSchema.Context)
    {

    }

    [XmlAttribute]
    public string DataType { get; set; }

    [XmlText]
    public string Value 
    { 
        get { return DataValue.GetValue().ToString(); }
        set
        {
            DataValue = AttributeValueFactory.Create(DataType, value);   
        }
    }

    public AttributeValue DataValue { get; set; }        
}

public abstract class AttributeValue
{
    public AttributeValue()
    {

    }
    public abstract object GetValue();
}

public class AttributeValue : AttributeValue
{
    public T Value { get; set; }
    public override object GetValue()
    {
        return Value;
    }
}

Und eine entsprechende Fabrikklassen, um den Attributwert zu erstellen:

public static AttributeValue Create(string xacmlDataType, string value)
    {

        AttributeValue _attributeValue = null;

        switch (xacmlDataType)
        {
            case "http://www.w3.org/2001/XMLSchema#string":
            case "http://www.w3.org/2001/XMLSchema#x500Name":
            case "http://www.w3.org/2001/XMLSchema#ipAddress":
            case "http://www.w3.org/2001/XMLSchema#dnsName":
            case "http://www.w3.org/2001/XMLSchema#xPathExpression":
                _attributeValue = new AttributeValue { Value = value };
                break;
            case "http://www.w3.org/2001/XMLSchema#boolean":
                _attributeValue = new AttributeValue {Value = XmlConvert.ToBoolean(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#integer":
                _attributeValue = new AttributeValue { Value = XmlConvert.ToInt32(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#double":
                _attributeValue = new AttributeValue { Value = XmlConvert.ToDouble(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#time":
            case "http://www.w3.org/2001/XMLSchema#date":
            case "http://www.w3.org/2001/XMLSchema#dateTime":
                _attributeValue = new AttributeValue { Value = XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.Utc) };
                break;
            case "http://www.w3.org/2001/XMLSchema#anyURI":
                _attributeValue = new AttributeValue { Value = new Uri(value) };
                break;
            case "http://www.w3.org/2001/XMLSchema#hexInteger":
                _attributeValue = new AttributeValue { Value = Encoding.ASCII.GetBytes(value) };
                break;          
            case "http://www.w3.org/2001/XMLSchema#dayTimeDuration":
            case "http://www.w3.org/2001/XMLSchema#yearMonthDuration":
                _attributeValue = new AttributeValue { Value = XmlConvert.ToTimeSpan(value) };
                break;                
            default:
                throw new NotImplementedException("Datentyp '" + xacmlDataType + "' wird nicht unterstützt.");
        }           

        return _attributeValue;
    }

Es tut mir leid, meine eigene Frage auf stackoverflow zu beantworten, aber manchmal passiert es.

Vielen Dank für die Antworten, Leute!

0voto

carlosfigueira Punkte 82509

Was du versuchst zu tun, kann wirklich nicht gemacht werden. Du hast eine dynamische Struktur (XML, dessen Datenfelder beliebige Typen haben können), und du möchtest eine stark typisierte Definition in deiner Klasse haben. Wenn es stark typisiert sein soll, solltest du den Typ zur Kompilierzeit kennen, was du nicht tust. Der Vorschlag von @caesay ist ein guter, oder einfach die Daten als Object darzustellen würde auch funktionieren, aber du kannst keine Compilerprüfung (d.h. starke Typisierung) haben, wenn du die Informationen nicht zur Kompilierzeit kennst.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X