Чи існує серійний загальний клас пари ключ / значення у .NET?


79

Я шукаю об’єкт пари ключ / значення, який я можу включити у веб-службу.

Я спробував використовувати .NET System.Collections.Generic.KeyValuePair<> клас , але він неправильно серіалізується у веб-службі. У веб-службі властивості Key і Value не серіалізовані, що робить цей клас марним, якщо хтось не знає способу виправити це.

Чи існує інший загальний клас, який можна використовувати для цієї ситуації?

Я б використовував System.Web.UI.Pairклас .NET , але він використовує Object для своїх типів. Було б непогано використовувати клас Generic, хоча б лише для безпеки типу.

Відповіді:


95

Просто визначте структуру / клас.

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

3
@Paddy: Знання того, як valuetypes стає хешованим, і порівняння рівності є обов'язковим
leppie

2
IDictionary можна серіалізувати зараз, у 4.5 (принаймні за допомогою JSON)
tomg

@Joe: Не соромтеся писати власний конструктор.
леппі

@leppie я зробив, але лише спостереження щодо цієї чудової відповіді.
Джо

Спробувавши це, я отримую цю помилку збірки. Будь ласка, надайте будь-які ідеї для виправлення. '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>>'
Картік

22

Я не думаю, що існує, оскільки Dictionary<>сам по собі не піддається серіалізації XML, коли мені довелося надіслати об'єкт словника через веб-службу, я в кінцевому підсумку обернув Dictionary<>об'єкт сам і додав підтримку 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 взагалі не згадує словник. Питання полягає в серіалізації пари ключ / значення. Ваша відповідь пов'язана, але я думаю, що це відволікає від фундаментальної проблеми.
Адам Ральф,

17

У цій публікації блогу MSDN ви знайдете причину, через яку неможливо серіалізувати KeyValuePairs

Відповідь Struct - це найпростіше рішення, проте не єдине рішення. "Кращим" рішенням є написання власного класу KeyValurPair, який можна серіалізувати.


9
Зауважте, що DataContractSerializer (у комплекті з .NET 3.0 та WCF) може чудово обробляти KeyValuePair <,>. Отже, це не загальна проблема серіалізації, а швидше проблема конкретного серіалізатора, який ви використовуєте (як пропонується посилання на сторінку MSDN).
Крістіан.

Ваше повідомлення в блозі MSDN ( blogs.msdn.microsoft.com/seshadripv/archive/2005/11/02/… ) тепер мертве посилання
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

У фреймворку 4.0 також додано сімейство класів Tuple, які можна серіалізувати та зрівняти. Можна використовувати Tuple.Create(a, b)або new Tuple<T1, T2>(a, b).


16
Хоча типи Tuple можна серіалізувати, вони, на жаль, не можна серіалізувати XML
Гепард

0

KeyedCollection - це тип словника, який можна безпосередньо серіалізувати в xml без жодної дурниці. Єдина проблема полягає в тому, що ви повинні отримувати доступ до значень за допомогою: coll ["key"]. Value;


Я не думаю, що KeyedCollection можна серіалізувати в WebService, оскільки він не має жодного публічного конструктора. Атрибут [Serializable] працює лише для віддаленого керування.
Мартін


0

Використовуйте DataContractSerializer, оскільки він може обробляти пару значень ключа.

    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 є моєю улюбленою колекцією для (виключно) обтікання даних, які слід серіалізувати в JSON, оскільки їх легко розширити без необхідності додаткових struct і діє як серіалізаційна заміна дляTuple<>[]

Можливо, це не найчистіший спосіб, але я волію включати та використовувати його безпосередньо в класах (які будуть серіалізовані), замість того, щоб оголошувати новий 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);
    }
}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.