.NET中是否有可序列化的通用键/值对类?


79

我正在寻找可以包含在Web服务中的键/值对对象。

我尝试使用.NET的System.Collections.Generic.KeyValuePair<>类,但无法在Web服务中正确序列化。在Web服务中,“键”和“值”属性未序列化,因此除非有人知道解决此问题的方法,否则该类无用。

还有其他通用类可以用于这种情况吗?

我会使用.NET的System.Web.UI.Pair类,但是它使用Object作为其类型。如果仅出于类型安全性考虑,使用Generic类会很好。

Answers:


95

只需定义一个结构/类。

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

3
@Paddy:必须知道如何散列值类型并比较相等性
嬉皮士2012年

2
IDictionary现在可以在4.5(至少使用JSON)中可序列化
tomg

@Joe:随时编写自己的构造函数。
leppie 2014年

@leppie我做到了,但是只是观察一下这个很好的答案。
2014年

尝试此操作后,出现此构建错误。请提供任何解决办法。'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本身不是可序列化的,当我需要通过Web服务发送字典对象时,我最终自己包装了该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答案是最简单的解决方案,但不是唯一的解决方案。一种“更好”的解决方案是编写一个可序列化的Custom KeyValurPair类。


9
请注意,DataContractSerializer(.NET 3.0和WCF附带)可以完美地处理KeyValuePair <,>。因此,这不是一般的序列化问题,而是您所使用的特定序列化程序的问题(如指向MSDN页面的链接所建议的)。
Christian.K,

您的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
虽然元组类型是可序列化的,但是不幸的是它们不是XML可序列化的
猎豹

0

KeyedCollection是一种字典类型,可以直接序列化为xml,而无需任何废话。唯一的问题是,您必须通过以下方式访问值:coll [“ key”]。Value;


我认为KeyedCollection不能在WebService中进行序列化,因为它没有任何公共构造函数。[可序列化]属性仅适用于远程处理。
马丁


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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.