Solved

Why does Dictionary<string, object> not serialize?

Posted on 2010-11-17
21
5,469 Views
Last Modified: 2012-05-10
I have the following classes:

[DataContract]
public class BaseField
{
  private string fieldName;
  private object fieldValue;

  public BaseField(string field, object value)
  {
    fieldName = field;
    fieldValue = value;
  }

  [DataMember]
  public string Name
  {
    get { return fieldName; }
    set { fieldName = value; }
  }

  [DataMember]
  public object Value
  {
    get { return fieldValue; }
    set { fieldValue = value; }
  }

  public string AsString
  {
    get
    {
      return Value.ToString();
    }
  }

  public int AsInteger
  {
      get
      {
        //if we cannot parse, return 0...
        int integer;
        if (!int.TryParse(AsString, out integer))
        {
          integer = 0;
        }
        return integer;
      }
    }
  }
}

[DataContract]
public class FieldCollection
{
  [DataMember]
  private Dictionary<string, BaseField> fieldCollection;
}

[DataContract]
public class TestCollection
{
  [DataMember]
  private Dictionary<string, object> fieldCollection;
}

[DataContract]
public class MyObject : FieldCollection
{
}

When deriving MyObject from FieldCollection I have issues with serialization (the pipe is closed).

When deriving MyObject from TestCollection I have 0 issues. Why can I not serialize FieldCollection? I know it is because I used BaseField in the Dictionary object, but BaseField serializes just fine otherwise. Why is the Dictionary with BaseField not be serializable? How do I get it to serialize?

Also, if try to put a BaseField into my TestCollection dictionary it has issues with the serialization.
0
Comment
Question by:hmstechsupport
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 11
  • 6
  • 3
  • +1
21 Comments
 
LVL 3

Expert Comment

by:kraiven
ID: 34156251
Works fine for me. I take all your classes above are marked serializable? How are you serializing?
0
 

Author Comment

by:hmstechsupport
ID: 34156282
[DataContract] is what marks it as serializable
0
 

Author Comment

by:hmstechsupport
ID: 34156302
kraiven, these classes compile no problem; so unless you test through a WCF pipe you won't be able to see the behavior I'm talking about.
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:hmstechsupport
ID: 34156364
Just to be sure I added "Serialization" to my BaseField:

[DataContract, Serialization]
public class BaseField

Made no difference.
0
 
LVL 3

Expert Comment

by:kraiven
ID: 34156394
Aah! Sorry I didn't realize you were talking about WCF objects. I'm afraid I'm not familiar with them.
0
 
LVL 18

Expert Comment

by:Richard Lee
ID: 34156574
Simple, dictionaries are not serializable. You will need to return a List<KeyValuePair<string, BaseField>> instead of a Dictionary<string, BaseField>.

You can easily convert a dictionary to and from a list. see below:

Dictionary to list: myDictionary.ToList();
List to Dictionary: myList.ToDictionary(x => x.Key, y => y.Value);

Remember you are converting to and from a list of KeyValuePairs (List<KeyValuePair<string, BaseField>>).

DaTribe
0
 
LVL 13

Expert Comment

by:Naman Goel
ID: 34156751
Hi,
you can create your own dictionary by deriving it from following class XmlDictionary and use it inplace of dictionary this will be serializable easily.

also you can refer to following link for one more method:

http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx
[CollectionDataContract(Name="MyDictionary", ItemName="MyDictionaryItem")]
public class XmlDictionary<TKey, TValue>
{
    /// <summary>
    /// Key/value pair.
    /// </summary>
    public struct DictionaryItem
    {
        /// <summary>
        /// Dictionary item key.
        /// </summary>
        public TKey Key;

        /// <summary>
        /// Dictionary item value.
        /// </summary>
        public TValue Value;
    }

    /// <summary>
    /// Dictionary items.
    /// </summary>
    public DictionaryItem[] Items
    {
        get {
            List<DictionaryItem> items = new List<DictionaryItem>(ItemsDictionary.Count);

            foreach (KeyValuePair<TKey, TValue> pair in ItemsDictionary) {
                DictionaryItem item;

                item.Key = pair.Key;
                item.Value = pair.Value;

                items.Add(item);
            }

            return (items.ToArray());
        }
        set {
            ItemsDictionary = new Dictionary<TKey,TValue>();

            foreach (DictionaryItem item in value)
                ItemsDictionary.Add(item.Key, item.Value);
        }
    }

    /// <summary>
    /// Indexer base on dictionary key.
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public TValue this[TKey key]
    {
        get {
            return (ItemsDictionary[key]);
        }
        set {
            Debug.Assert(value != null);
            ItemsDictionary[key] = value;
        }
    }

    /// <summary>
    /// Delegate for get key from a dictionary value.
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public delegate TKey GetItemKeyDelegate(TValue value);

    /// <summary>
    /// Add a range of values automatically determining the associated keys.
    /// </summary>
    /// <param name="values"></param>
    /// <param name="keygen"></param>
    public void AddRange(IEnumerable<TValue> values, GetItemKeyDelegate keygen)
    {
        foreach (TValue v in values)
            ItemsDictionary.Add(keygen(v), v);
    }

    /// <summary>
    /// Items dictionary.
    /// </summary>
    [XmlIgnore]
    public Dictionary<TKey, TValue> ItemsDictionary = new Dictionary<TKey,TValue>();
}

Open in new window

0
 

Author Comment

by:hmstechsupport
ID: 34156766
Dictionaries ARE serializable. If I choose to use something like Dictionary<string, string> I even no problems serializing that. Even Dictionary<string, object> serializes, except when I try to put BaseField as the object.

That being said, I'm going to try converting to list and back and see what happens.
0
 

Author Comment

by:hmstechsupport
ID: 34156777
Apparently there is no such thing as Dictionary.ToList()
0
 
LVL 13

Expert Comment

by:Naman Goel
ID: 34156778
The custom class XmlDictionary<TKey, TValue> is also doing the same.. so you can try that.
0
 
LVL 3

Expert Comment

by:kraiven
ID: 34156820
hmstechsupport:

There is a ToList() method on Dictionary.
0
 

Author Comment

by:hmstechsupport
ID: 34156840
No ToList() for me using .NET 3.5 VS2008
0
 
LVL 3

Expert Comment

by:kraiven
ID: 34156915
Well I think ToList is part of the Linq enhancements which are available for .Net 3+.
0
 

Author Comment

by:hmstechsupport
ID: 34156947
Not using Linq at all.
0
 
LVL 3

Expert Comment

by:kraiven
ID: 34157012
You don't need to explicitly, but if you add a "using System.Linq" statement to your code you will get the Dictionary.ToList() extension method.
0
 
LVL 13

Expert Comment

by:Naman Goel
ID: 34157029
Yes ToList method is introduced with :

Version Information
.NET Framework
Supported in: 4, 3.5
.NET Framework Client Profile
Supported in: 4, 3.5 SP1

as part of Linq.

http://msdn.microsoft.com/en-us/library/bb342261.aspx

you can just convert item to list by iterating through each item of dictionary and storing value in list.
0
 
LVL 3

Expert Comment

by:kraiven
ID: 34157075
Although dict.ToList() has got to be an improvement on that. ;-)
0
 

Author Comment

by:hmstechsupport
ID: 34157262
Using a List<KeyValuePair<string, BaseField>> and converting to a dictionary when I need it also does not work. It seems trying to use BaseField in ANY collection whether it is List, Dictionary, KeyValuePair, will not serialize. I guess this is just not a viable solution. Thanks Microsoft!
0
 

Author Comment

by:hmstechsupport
ID: 34173402
I think I finally figured out what was causing the problem and it's simply amazing. Let's take a look at my BaseField constructor:

public BaseField(string field, object value)
{
    fieldName = field;
    fieldValue = value;
}

We pass "object value" when creating a BaseField. Let me give an example of this:

DataTable table = LoadRecordFromDataBase();

DataRow row = table.Rows[0];

Dictionary<string, BaseField> fieldCollection = new Dictionary<string, BaseField>();
foreach (DataColumn column in table.Columns)
{
    BaseField theField = new BaseField(column.ColumnName, row[column.ColumnName]);

    fieldCollection.Add(column.ColumnName, theField);
}


This is a very simple example of how my field collection would be created. Notice how we pass row[column.ColumnName] as the object when creating the BaseField. It turns out if the value of row[column.ColumnName] is BDNull, this causes the collection to fail during serialization.

To avoid the serialization issue I have to pass row[column.ColumnName].ToString() when creating the BaseField. Simply amazing!
0
 

Accepted Solution

by:
hmstechsupport earned 0 total points
ID: 34173782
Another solution would be to mark DBNull as a KnownType:

    [DataContract]
    [KnownType(typeof(System.DBNull))]
    public class Field
    {
    }

This also corrects my serialization issues.
0
 

Author Closing Comment

by:hmstechsupport
ID: 34203665
Thanks to all who posted.
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

In order to hide the "ugly" records selectors (triangles) in the rowheaders, here are some suggestions. Microsoft doesn't have a direct method/property to do it. You can only hide the rowheader column. First solution, the easy way The first sol…
This article aims to explain the working of CircularLogArchiver. This tool was designed to solve the buildup of log file in cases where systems do not support circular logging or where circular logging is not enabled
Finding and deleting duplicate (picture) files can be a time consuming task. My wife and I, our three kids and their families all share one dilemma: Managing our pictures. Between desktops, laptops, phones, tablets, and cameras; over the last decade…
This video shows how to use Hyena, from SystemTools Software, to update 100 user accounts from an external text file. View in 1080p for best video quality.

734 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question