Есть ли в .NET сериализуемый универсальный класс пары ключ / значение? - PullRequest
76 голосов
/ 17 сентября 2008

Я ищу объект пары ключ / значение, который можно включить в веб-сервис.

Я пытался использовать класс .NET System.Collections.Generic.KeyValuePair<>, но он неправильно сериализуется в веб-сервисе. В веб-службе свойства Key и Value не сериализуются, что делает этот класс бесполезным, если кто-то не знает, как это исправить.

Есть ли другой универсальный класс, который можно использовать в этой ситуации?

Я бы использовал .NET System.Web.UI.Pair класс, но он использует Object для своих типов. Было бы неплохо использовать класс Generic хотя бы для обеспечения безопасности типов.

Ответы [ 10 ]

91 голосов
/ 17 сентября 2008

Просто определите структуру / класс.

[Serializable]
public struct KeyValuePair<K,V>
{
  public K Key {get;set;}
  public V Value {get;set;}
}
22 голосов
/ 17 сентября 2008

Я не думаю, что 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
}
17 голосов
/ 20 января 2009

Вы найдете причину, по которой KeyValuePairs нельзя сериализовать в этом сообщении блога MSDN

Ответ Struct - самое простое решение, но не единственное. «Лучшее» решение - написать пользовательский класс KeyValurPair с возможностью сериализации.

6 голосов
/ 21 февраля 2014
 [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 голос
/ 05 января 2011

В 4.0 Framework также добавлено семейство классов Tuple, которые сериализуемы и уравновешены. Вы можете использовать Tuple.Create(a, b) или new Tuple<T1, T2>(a, b).

0 голосов
/ 10 мая 2019

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);
    }
}
0 голосов
/ 23 апреля 2018

Используйте 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 голосов
/ 17 июня 2016

XmlSerializer не работает со словарями. О, и у него тоже есть проблемы с KeyValuePairs

http://www.codeproject.com/Tips/314447/XmlSerializer-doesnt-work-with-Dictionaries-Oh-and

0 голосов
/ 04 ноября 2011

Вы можете использовать Tuple<string,object>

см. Подробности об использовании Tuple: Работа с Tuple в C # 4.0

0 голосов
/ 17 сентября 2008

KeyedCollection - это тип словаря, который можно напрямую сериализовать в XML без всякой чуши. Единственная проблема заключается в том, что вы должны получить доступ к значениям с помощью: coll ["key"]. Value;

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...