Gibt es in .NET eine serialisierbare generische Schlüssel / Wert-Paarklasse?

79

Ich suche nach einem Schlüssel / Wert-Paar-Objekt, das ich in einen Webdienst aufnehmen kann.

Ich habe versucht, die .NET- System.Collections.Generic.KeyValuePair<>Klasse zu verwenden, aber sie wird in einem Webdienst nicht ordnungsgemäß serialisiert. In einem Webdienst werden die Eigenschaften "Schlüssel" und "Wert" nicht serialisiert, wodurch diese Klasse unbrauchbar wird, es sei denn, jemand kennt eine Möglichkeit, dies zu beheben.

Gibt es eine andere generische Klasse, die für diese Situation verwendet werden kann?

Ich würde die .NET- System.Web.UI.PairKlasse verwenden, aber sie verwendet Object für ihre Typen. Es wäre schön, eine generische Klasse zu verwenden, wenn auch nur aus Gründen der Typensicherheit.

Dan Herbert
quelle

Antworten:

95

Definieren Sie einfach eine Struktur / Klasse.

[Serializable]
public struct KeyValuePair<K,V>
{
  public K Key {get;set;}
  public V Value {get;set;}
}
Leppie
quelle
3
@Paddy: Zu wissen, wie Werttypen gehasht werden und Gleichheit zu vergleichen, ist ein Muss
leppie
2
IDictionary ist jetzt serialisierbar, in 4.5 (zumindest mit JSON)
Tomg
@ Joe: Fühlen Sie sich frei, Ihren eigenen Konstruktor zu schreiben.
Leppie
@leppie habe ich gemacht, aber nur eine Beobachtung zu dieser ansonsten tollen Antwort.
Joe
Nachdem ich dies versucht habe, erhalte ich diesen Build-Fehler. Bitte geben Sie Ideen zur Behebung an. 'AttributeCollection' does not contain a definition for 'Where' and the best extension method overload 'Queryable.Where<KeyValuePair<string, object>>(IQueryable<KeyValuePair<string, object>>, Expression<Func<KeyValuePair<string, object>, bool>>)' requires a receiver of type 'IQueryable<KeyValuePair<string, object>>'
Karthik
22

Ich glaube nicht, dass Dictionary<>XML an sich nicht serialisierbar ist. Als ich ein Wörterbuchobjekt über einen Webdienst senden musste, habe ich das Dictionary<>Objekt selbst verpackt und Unterstützung für hinzugefügt IXMLSerializable.

/// <summary>
/// Represents an XML serializable collection of keys and values.
/// </summary>
/// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
/// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{
    #region Constants

    /// <summary>
    /// The default XML tag name for an item.
    /// </summary>
    private const string DEFAULT_ITEM_TAG = "Item";

    /// <summary>
    /// The default XML tag name for a key.
    /// </summary>
    private const string DEFAULT_KEY_TAG = "Key";

    /// <summary>
    /// The default XML tag name for a value.
    /// </summary>
    private const string DEFAULT_VALUE_TAG = "Value";

    #endregion

    #region Protected Properties

    /// <summary>
    /// Gets the XML tag name for an item.
    /// </summary>
    protected virtual string ItemTagName
    {
        get
        {
            return DEFAULT_ITEM_TAG;
        }
    }

    /// <summary>
    /// Gets the XML tag name for a key.
    /// </summary>
    protected virtual string KeyTagName
    {
        get
        {
            return DEFAULT_KEY_TAG;
        }
    }

    /// <summary>
    /// Gets the XML tag name for a value.
    /// </summary>
    protected virtual string ValueTagName
    {
        get
        {
            return DEFAULT_VALUE_TAG;
        }
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Gets the XML schema for the XML serialization.
    /// </summary>
    /// <returns>An XML schema for the serialized object.</returns>
    public XmlSchema GetSchema()
    {
        return null;
    }

    /// <summary>
    /// Deserializes the object from XML.
    /// </summary>
    /// <param name="reader">The XML representation of the object.</param>
    public void ReadXml(XmlReader reader)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        bool wasEmpty = reader.IsEmptyElement;

        reader.Read();

        if (wasEmpty)
        {
            return;
        }

        while (reader.NodeType != XmlNodeType.EndElement)
        {
            reader.ReadStartElement(ItemTagName);

            reader.ReadStartElement(KeyTagName);
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();

            reader.ReadStartElement(ValueTagName);
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();

            this.Add(key, value);

            reader.ReadEndElement();
            reader.MoveToContent();
        }

        reader.ReadEndElement();
    }

    /// <summary>
    /// Serializes this instance to XML.
    /// </summary>
    /// <param name="writer">The writer to serialize to.</param>
    public void WriteXml(XmlWriter writer)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        foreach (TKey key in this.Keys)
        {
            writer.WriteStartElement(ItemTagName);

            writer.WriteStartElement(KeyTagName);
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();

            writer.WriteStartElement(ValueTagName);
            TValue value = this[key];
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }

    #endregion
}
Kompilieren Sie dies
quelle
5
OP erwähnt Dictionary überhaupt nicht. Die Frage betrifft die Serialisierung eines Schlüssel / Wert-Paares. Ihre Antwort ist verwandt, aber ich denke, sie beeinträchtigt das grundlegende Problem.
Adam Ralph
17

Den Grund, warum KeyValuePairs nicht serialisiert werden können, finden Sie in diesem MSDN-Blogbeitrag

Die Strukturantwort ist die einfachste Lösung, jedoch nicht die einzige. Eine "bessere" Lösung besteht darin, eine benutzerdefinierte KeyValurPair-Klasse zu schreiben, die serialisierbar ist.

user56931
quelle
9
Beachten Sie, dass der DataContractSerializer (wie in .NET 3.0 und WCF enthalten) KeyValuePair <,> perfekt verarbeiten kann. Es handelt sich also nicht um ein allgemeines Serialisierungsproblem, sondern um ein Problem des von Ihnen verwendeten Serializers (wie der Link zur MSDN-Seite nahelegt).
Christian.K
Ihr MSDN-Blog-Beitrag ( blogs.msdn.microsoft.com/seshadripv/archive/2005/11/02/… ) ist jetzt ein toter Link
Brewmanz
6
 [Serializable]
 public class SerializableKeyValuePair<TKey, TValue>
    {

        public SerializableKeyValuePair()
        {
        }

        public SerializableKeyValuePair(TKey key, TValue value)
        {
            Key = key;
            Value = value;
        }

        public TKey Key { get; set; }
        public TValue Value { get; set; }

    }
GregoryBrad
quelle
1

Im 4.0-Framework wurde außerdem die Tuple-Klassenfamilie hinzugefügt, die serialisierbar und gleichwertig ist. Sie können Tuple.Create(a, b)oder verwenden new Tuple<T1, T2>(a, b).

Peter Oehlert
quelle
16
Während die Tupeltypen serialisierbar sind, sind sie leider nicht XML-serialisierbar
Cheetah
0

Eine KeyedCollection ist eine Art Wörterbuch, das ohne Unsinn direkt in XML serialisiert werden kann. Das einzige Problem ist, dass Sie auf Werte zugreifen müssen, indem Sie: coll ["key"]. Value;


quelle
Ich glaube nicht, dass KeyedCollection innerhalb eines WebService serialisiert werden kann, da es keinen öffentlichen Konstruktor hat. Das Attribut [Serializable] funktioniert nur zum Remoting.
Martin
0

Verwenden Sie den DataContractSerializer, da er das Schlüsselwertpaar verarbeiten kann.

    public static string GetXMLStringFromDataContract(object contractEntity)
    {
        using (System.IO.MemoryStream writer = new System.IO.MemoryStream())
        {
            var dataContractSerializer = new DataContractSerializer(contractEntity.GetType());
            dataContractSerializer.WriteObject(writer, contractEntity);
            writer.Position = 0;
            var streamReader = new System.IO.StreamReader(writer);
            return streamReader.ReadToEnd();
        }
    }
Hasse
quelle
0

DataTableist meine Lieblingssammlung für (ausschließlich) Wrapping-Daten, die in JSON serialisiert werden sollen, da sie einfach erweitert werden können, ohne dass zusätzliche Funktionen erforderlich sind, structund sich wie ein serialisierbarer Ersatz für JSON verhaltenTuple<>[]

Vielleicht nicht der sauberste Weg, aber ich ziehe es vor, ihn direkt in die Klassen aufzunehmen und zu verwenden (die serialisiert werden sollen), anstatt einen neuen zu deklarieren struct

class AnyClassToBeSerialized
{
    public DataTable KeyValuePairs { get; }

    public AnyClassToBeSerialized
    {
        KeyValuePairs = new DataTable();
        KeyValuePairs.Columns.Add("Key", typeof(string));
        KeyValuePairs.Columns.Add("Value", typeof(string));
    }

    public void AddEntry(string key, string value)
    {
        DataRow row = KeyValuePairs.NewRow();
        row["Key"] = key; // "Key" & "Value" used only for example
        row["Value"] = value;
        KeyValuePairs.Rows.Add(row);
    }
}
Teodor Tite
quelle
-3

Sie können verwenden Tuple<string,object>

Weitere Informationen zur TupleVerwendung finden Sie hier: Arbeiten mit Tupel in C # 4.0

Saraf Talukder
quelle
16
Dies wurde bereits vorgeschlagen . Leider ist die Tuple-Klasse nicht XML-serialisierbar.
Dan Herbert