5

I'm using a dictionary to hold some parameters and I've just found out that it's not possible to serialize anything that implements IDictionary (unable to serialize IDictionary).

As a workaround I'd like to convert may dictionary into a string for serialization and then convert back to a dictionary when required.

As I'm trying to improve my LINQ this seems like a good place to do it but I'm not sure how to start.

This is how I'd implement it without LINQ:

/// <summary>
/// Get / Set the extended properties of the FTPS processor
/// </summary>
/// <remarks>Can't serialize the Dictionary object so convert to a string (http://msdn.microsoft.com/en-us/library/ms950721.aspx)</remarks>
public Dictionary<string, string> FtpsExtendedProperties
{
    get 
    {
        Dictionary<string, string> dict = new Dictionary<string, string>();

        // Get the Key value pairs from the string
        string[] kvpArray = m_FtpsExtendedProperties.Split('|');

        foreach (string kvp in kvpArray)
        {
            // Seperate the key and value to build the dictionary
            string[] pair = kvp.Split(',');
            dict.Add(pair[0], pair[1]);
        }

        return dict; 
    }

    set 
    {
        string newProperties = string.Empty;

        // Iterate through the dictionary converting the value pairs into a string
        foreach (KeyValuePair<string,string> kvp in value)
        {
            newProperties += string.Format("{0},{1}|", kvp.Key, kvp.Value);    
        }

        // Remove the last pipe serperator
        newProperties = newProperties.Substring(0, newProperties.Length - 1);
    }
}
Community
  • 1
  • 1
TeamWild
  • 2,460
  • 8
  • 43
  • 53
  • as an aside you can 'Linq' the string generation: `string.Join("|", value.Select(kvp => string.Format("{0},{1}", kvp.Key, kvp.Value)))` (but in .Net 3.5 you need a `.ToArray()` on the `Select()`) – Andras Zoltan Sep 05 '11 at 10:43

6 Answers6

10

try something like this

var dict = str.Split(';')
              .Select(s => s.Split(':'))
              .ToDictionary(a => a[0].Trim(), a => a[1].Trim()));

above one is true for the following kind of string

"mykey1:myvalue1; mykey2:value2;...."
Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
3

In the context of your code

/// Get / Set the extended properties of the FTPS processor
/// </summary>
/// <remarks>Can't serialize the Dictionary object so convert to a string (http://msdn.microsoft.com/en-us/library/ms950721.aspx)</remarks>
public Dictionary<string, string> FtpsExtendedProperties
{
get 
{

Dictionary<string, string> dict = m_FtpsExtendedProperties.Split('|')
      .Select(s => s.Split(','))
      .ToDictionary(key => key[0].Trim(), value => value[1].Trim());

    return dict; 
}

set 
{

        // NOTE: for large dictionaries, this can use
        // a StringBuilder instead of a string for cumulativeText

        // does not preserve Dictionary order (if that is important - reorder the String.Format)
    string newProperties = 
              value.Aggregate ("",
                      (cumulativeText,kvp) => String.Format("{0},{1}|{2}", kvp.Key, kvp.Value, cumulativeText));

        // Remove the last pipe serperator
        newProperties = newProperties.Substring(0, newProperties.Length - 1);

        }
    }

Haven't tested this, but the functions used should give you some idea of how to do it fairly succinctly with LINQ

Neil Fenwick
  • 6,106
  • 3
  • 31
  • 38
  • The Dictionary object doesn't have an Aggregate method. I'd been trying this approach myself but not having a lot of luck. – TeamWild Sep 05 '11 at 12:35
  • @TeamWild, have you added a "using System.Linq;" to the top of your class file? – Neil Fenwick Sep 05 '11 at 12:39
  • Nice approach to the get accessor. I like the way you have separated the key and value so that additional processing can be applied (trim). – TeamWild Sep 05 '11 at 13:58
3

Another option is to use linq-to-xml to do the heavy lifting to make sure everything parses correctly.

Starting with:

var dict = new Dictionary<string, string>()
{
    { "A", "a" },
    { "B", "b" },
    { "C", "c" },
};

You can turn this into xml via:

var xe = new XElement("d",
    from kvp in dict
    select new XElement("p",
        new XAttribute("k", kvp.Key),
        new XAttribute("v", kvp.Value))).ToString();

Which becomes:

<d>
  <p k="A" v="a" />
  <p k="B" v="b" />
  <p k="C" v="c" />
</d>

To turn this back into the dictionary use this:

var dict2 = XDocument
    .Parse(xml)
    .Root
    .Elements("p")
    .ToDictionary(
        x => x.Attribute("k").Value,
        x => x.Attribute("v").Value);

Easy, huh?

This method will avoid the need to specifically escape special characters such as "|" or ";".

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • This is an interesting approach to allowing Dictionary pairs to be serialized. It seems a little heavyweight for my current problem but I will keep it in mind for the future. Thanks. – TeamWild Sep 06 '11 at 07:38
  • @TeamWild - keep in mind that any approach that uses splitting, joining, parsing, etc, are all risky if you get your special characters in your strings. Using linq-to-xml removes this risk and for even quite large dictionaries is very fast and quite efficient. – Enigmativity Sep 06 '11 at 23:59
1

Try following code

string vals = "a|b|c|d|e";
var dict = vals.Split('|').ToDictionary(x=>x);

dict will give you a dictionary containing five entries.

Muhammad Adeel Zahid
  • 17,474
  • 14
  • 90
  • 155
1

For the purpose of you learning LINQ I've include the serialization as well

var serialized = string.Join("|",from pair in value
                                 select string.Format("{0},{1}", pair.Key, pair.Value);

var deserialized = new Dictionary<string,string(
                       from pair in serialized.Split("|")
                       let tokens = pair.Split(",")
                       select new KeyValuePair<string,string>(tokens[0],tokens[1]));
Rune FS
  • 21,497
  • 7
  • 62
  • 96
0

One option would be to do:

  1. 'Convert' the Dictionary to a NameValueCollection
  2. Use the below code to convert the NameValueCollection into a HttpValueCollection.

    var parserCollection = HttpUtility.ParseQueryString(string.Empty); parserCollection.Add(yourNameValueCollection);

  3. parserCollection.ToString() will then generate a (url encoded) string version of your dictionary.

To convert the string form back to a NameValueCollection, use HttpUtility.ParseQueryString(stringFormOfYourDictionary). Then do the reverse of step 1 to convert the NameValueCollection back to a Dictionary.

The advantage of this is that it will work regardless of the contents of the Dictionary. There is no dependence on the format of the data (so it will work fine regardless of the values of the keys or values in the dictionary).

Community
  • 1
  • 1
mjwills
  • 23,389
  • 6
  • 40
  • 63