Czy istnieje możliwość serializacji ogólnej pary klucz / wartość w .NET?


79

Szukam obiektu pary klucz / wartość, który mogę dołączyć do usługi internetowej.

Próbowałem użyć System.Collections.Generic.KeyValuePair<>klasy .NET , ale nie jest ona poprawnie serializowana w usłudze sieciowej. W usłudze sieci Web właściwości klucza i wartości nie są serializowane, co sprawia, że ​​ta klasa jest bezużyteczna, chyba że ktoś zna sposób, aby to naprawić.

Czy istnieje inna klasa ogólna, której można użyć w tej sytuacji?

Użyłbym System.Web.UI.Pairklasy .NET , ale używa ona Object dla swoich typów. Byłoby miło użyć klasy Generic, choćby ze względu na bezpieczeństwo typów.

Odpowiedzi:


95

Po prostu zdefiniuj strukturę / klasę.

[Serializable]
public struct KeyValuePair<K,V>
{
  public K Key {get;set;}
  public V Value {get;set;}
}

3
@Paddy: Wiedząc, jak valuetypes zostanie zakodowane i porównywania równości jest koniecznością
leppie

2
IDictionary można teraz serializować w wersji 4.5 (przynajmniej z JSON)
tomg

@Joe: Zapraszam do napisania własnego konstruktora.
leppie

@leppie Zrobiłem, ale tylko obserwacja na temat tej świetnej odpowiedzi.
Joe

Po wypróbowaniu tego otrzymuję ten błąd kompilacji. Podaj pomysły do ​​rozwiązania. '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

Wydaje mi się, że nie ma, ponieważ Dictionary<>sam w sobie nie jest możliwy do serializacji XML, kiedy musiałem wysłać obiekt słownika za pośrednictwem usługi sieciowej, w końcu sam zawinąłem Dictionary<>obiekt i dodałem obsługę 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
}

5
OP w ogóle nie wspomina o słowniku. Pytanie dotyczy serializacji pary klucz / wartość. Twoja odpowiedź jest powiązana, ale myślę, że umniejsza to podstawową kwestię.
Adam Ralph

17

W tym poście na blogu MSDN znajdziesz powód, dla którego nie można serializować par kluczy KeyValuePairs

Odpowiedź Struct jest najprostszym rozwiązaniem, ale nie jedynym rozwiązaniem. „Lepszym” rozwiązaniem jest napisanie klasy Custom KeyValurPair z możliwością serializacji.


9
Należy zauważyć, że DataContractSerializer (tak jak jest dostarczany z .NET 3.0 i WCF) może doskonale obsługiwać KeyValuePair <,>. Nie jest to więc ogólny problem z serializacją, ale raczej kwestia konkretnego serializatora, którego używasz (jak sugeruje łącze do strony MSDN).
Christian.K

Twój post na blogu MSDN ( blogs.msdn.microsoft.com/seshadripv/archive/2005/11/02/… ) jest teraz martwym linkiem
brewmanz

7
 [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; }

    }

1

W ramach 4.0 Framework jest również dodana rodzina klas Tuple, które można serializować i wyrównywać. Możesz użyć Tuple.Create(a, b)lub new Tuple<T1, T2>(a, b).


16
Chociaż typy krotki można serializować, niestety nie można ich serializować w formacie XML
Cheetah

0

KeyedCollection to typ słownika, który można bezpośrednio serializować do XML bez żadnych bzdur. Jedynym problemem jest to, że musisz uzyskać dostęp do wartości przez: coll ["klucz"]. Wartość;


Nie sądzę, aby KeyedCollection można było serializować w ramach usługi WebService, ponieważ nie ma ona żadnego publicznego konstruktora. Atrybut [Serializable] działa tylko w przypadku komunikacji zdalnej.
Martin


0

Użyj DataContractSerializer, ponieważ może obsługiwać parę klucz-wartość.

    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();
        }
    }

0

DataTable jest moją ulubioną kolekcją do (wyłącznie) opakowywania danych, które mają być serializowane do formatu JSON, ponieważ można je łatwo rozszerzyć bez potrzeby dodatkowego struct i działa jak serializowalny zamiennik dlaTuple<>[]

Może nie jest to najczystszy sposób, ale wolę włączać i używać go bezpośrednio w klasach (które mają być serializowane), zamiast deklarować nowy 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);
    }
}

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.