2008-09-17 13 views
73

Estoy buscando un objeto de par clave/valor que pueda incluir en un servicio web.¿Existe una clase de par clave/valor genérica serializable en .NET?

Intenté utilizar la clase System.Collections.Generic.KeyValuePair<> de .NET, pero no se serializa correctamente en un servicio web. En un servicio web, las propiedades de clave y valor no están serializadas, lo que hace que esta clase sea inútil, a menos que alguien sepa cómo arreglar esto.

¿Hay alguna otra clase genérica que pueda usarse para esta situación?

Usaría la clase System.Web.UI.Pair de .NET, pero usa Object para sus tipos. Sería bueno usar una clase Genérica, aunque solo sea para la seguridad del tipo.

Respuesta

87

Simplemente defina una estructura/clase.

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

simple. Cuando te paras a pensarlo ... – Paddy

+3

@Paddy: saber cómo se obtiene el valor de los tipos y modificar la igualdad es imprescindible – leppie

+2

IDictionary ahora es serializable, en 4.5 (al menos con JSON) – tomg

20

no creo que hay que Dictionary<> sí no es XML serializable, cuando tuve que enviar un objeto de diccionario a través de un servicio web que terminó envolviendo el objeto Dictionary<> mí mismo y añadiendo soporte para 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 no menciona en absoluto el diccionario. La cuestión es sobre la serialización de un par clave/valor. Su respuesta está relacionada , pero creo que desvirtúa la cuestión fundamental. –

0

A KeyedCollection es un tipo de diccionario que se puede serializar directamente a xml sin ningún tipo de tonterías. El único problema es que debe acceder a los valores por: coll ["key"]. Value;

+0

No creo que KeyedCollection se pueda serializar dentro de ** WebService ** porque no tiene ningún constructor público. El atributo [Serializable] solo funciona para la comunicación remota. – Martin

16

Va a encontrar la razón por la KeyValuePairs no pueden ser serializados en este MSDN Blog Post

La respuesta Struct es la solución más simple, sin embargo no es la única solución. Una solución "mejor" es escribir una clase Custom KeyValurPair que sea Serializable.

+9

Tenga en cuenta que el DataContractSerializer (como viene con .NET 3.0 y WCF) puede manejar perfectamente KeyValuePair <,>.Por lo tanto, no es un problema de serialización general, sino más bien un problema del serializador específico que utiliza (como sugiere el enlace a la página de MSDN). –

1

En el Marco 4.0, también está la adición de la familia de clases Tuple que son serializables y equivalentes. Puede usar Tuple.Create(a, b) o new Tuple<T1, T2>(a, b).

+16

Mientras que los tipos Tuple son serializables, desafortunadamente no son XML Serializable – Cheetah

-3

Puede utilizar Tuple<string,object>

ver esto para más detalles sobre el uso Tuple: Working with Tuple in C# 4.0

+15

Esto ya fue [ya sugerido] (http://stackoverflow.com/questions/83232/is-there-a-serializable-generic-key-value-pair- class-in-net/4598558 # 4598558). Desafortunadamente, la clase Tuple no es XML serializable. –

+0

Gracias Dan Herbert –

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

    } 
Cuestiones relacionadas