8

I have a Dictionary object and I want to write to disk and be able to read it from disk. Ideally I would avoid any 3rd party libraries. Is there a simple way to do this with regular C# 4?

ANSWER ACCEPTED.

Summary:

OPTION 1 - Using JavaScriptSerializer

Pros: No 3rd party library necessary. Also, uses more modern format, i.e. JSON

Cons: Difficult to read -- not formated. Also, does require reference to less commonly used System.Web.Extension assembly, even when the application has nothing to do with the web.

Solution:

Write:

File.WriteAllText("SomeFile.Txt", new JavaScriptSerializer().Serialize(dictionary));

Read:

var dictionary = new JavaScriptSerializer()
    .Deserialize<Dictionary<string, string>>(File.ReadAllText("SomeFile.txt"));

OPTION 2 - Using Linq to Xml

Pros: No 3rd party library necessary. Typically doesn't require adding additional references. Easily readable.

Cons: XML is not as preferable as something more modern such JSON.

Write:

new XElement("root", d.Select(kv => new XElement(kv.Key, kv.Value)))
            .Save(filename, SaveOptions.OmitDuplicateNamespaces);

Read:

var dictionary = XElement.Parse(File.ReadAllText(filename))
                .Elements()
                .ToDictionary(k => k.Name.ToString(), v => v.Value.ToString());

OPTION 3 - Use JSON.NET

Pros: Human readable. Modern format.

Cons: 3rd party library necessary.

Solution:

Write:

File.WriteAllText("SomeFile.Txt", JsonConvert.SerializeObject(dictionary));

Read:

var dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>
    (File.ReadAllText("SomeFile.txt"));
zumalifeguard
  • 8,648
  • 5
  • 43
  • 56
  • 2
    A simple serialization/deserialization would work. For ex, using *JavaScriptSerializer* is just one line of code. – L.B Nov 19 '14 at 19:37
  • 1
    You could also use XML serialization: http://web.archive.org/web/20100703052446/http://blogs.msdn.com/b/psheill/archive/2005/04/09/406823.aspx – gcarvelli Nov 19 '14 at 19:38
  • killermonkey50, thank you. I noticed that the older built-in serializer for xml doesn't support dictionaries, so you have to create your own wrappers like you indicated. I think it's better to use LINQ since the code is a lot smaller at least for the specific use case I'm talking about. – zumalifeguard Nov 19 '14 at 19:42
  • @killermonkey50 *IDictionary* is not supported by *XmlSerializer*. – L.B Nov 19 '14 at 19:42
  • What's the general SO attitude towards questions like this, where someone asks a question to give an answer? – Jonesopolis Nov 19 '14 at 19:44
  • L.B, I like your JavaScriptSerializer solution. It's nice that it's built in. Have you tried it with Dictionaries? I don't know why it wouldn't work, but strangers things have happened. – zumalifeguard Nov 19 '14 at 19:45
  • @zumalifeguard If you want I can post an answer. – L.B Nov 19 '14 at 19:46
  • Jonesy, it's actively encouraged as a way to store your own information, and also share with others. See: http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/ – zumalifeguard Nov 19 '14 at 19:46
  • Yes, L.B, post your answer – zumalifeguard Nov 19 '14 at 19:48
  • 1
    @L.B. I know, that solution creates an Entry class and uses lists to store it. Not the most elegant way to do it, but it works. – gcarvelli Nov 19 '14 at 19:52

3 Answers3

11

Without a Third Party like JSON.Net, Use JavaScriptSerializer:

File.WriteAllText("SomeFile.Txt", new JavaScriptSerializer().Serialize(dictionary));

Getting dictionary back from file:

var dictionary = new JavaScriptSerializer()
    .Deserialize<Dictionary<string, string>>(File.ReadAllText("SomeFile.txt"));

Only thing to remember is to add reference to System.Web.Extensions under project references and then you will be able to use JavaScriptSerializer after using System.Web.Script.Serialization;


Or with JSON.Net you can serialize your dictionary to JSON and then write it to file and then deserialize it, like:

Dictionary<string, string> dictionary = new Dictionary<string, string>();
dictionary.Add("1", "Some value 1");
dictionary.Add("2", "Something");

Storing Dictionary in file:

string json = JsonConvert.SerializeObject(dictionary);
File.WriteAllText("SomeFile.Txt", json);

Getting Dictionary back from file:

Dictionary<string, string> previousDictionary =
 JsonConvert.DeserializeObject<Dictionary<string, string>>
                                 (File.ReadAllText("SomeFile.txt"));

For comparison between the two options see: JSON.NET JsonConvert vs .NET JavaScriptSerializer

Community
  • 1
  • 1
Habib
  • 219,104
  • 29
  • 407
  • 436
  • 1
    From question: `I would avoid any 3rd party libraries.` – L.B Nov 19 '14 at 19:43
  • Habib, I understand this works but a better solution would be not require a 3rd party library. – zumalifeguard Nov 19 '14 at 19:50
  • 1
    @zumalifeguard, modified the answer to include .Net *only* option. – Habib Nov 19 '14 at 19:56
  • @zumalifeguard, umm what do you mean, [JavaScriptSerializer](http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx) is not a third party, it is provided with .Net framework. – Habib Nov 19 '14 at 19:58
  • Habib, thank you for your answer. Can I ask you to please remove references to JSON.NET in your answer? It's not clear why it's being referred. It reads like you're indicating two different ways to do it, but if that's the case, you should be more explicit, "I'm recommending two solutions", and indicate which is which. It will be easier for readers. My preference would be to only use the JavaScriptSerializer version. – zumalifeguard Nov 19 '14 at 20:01
  • @zumalifeguard, I have added a link for comparison between JSON.Net and .Net option, personally I use JSON.Net because I find it easier . – Habib Nov 19 '14 at 20:05
  • Habib, thank you. I use JSON.Net exclusively. I didn't even realize JavaScriptSerializer existed until L.B mentioned it above. Thanks again for your answer. Will decide on accepting shortly. – zumalifeguard Nov 19 '14 at 20:46
  • I'm trying the various solutions. I like the JavaScriptSerializer so far that's my preferred. – zumalifeguard Nov 19 '14 at 21:03
  • JavaScriptSerializer requires System.Web.Extension to be included in your project. Not sure if it's a big deal. I don't think it is. JavaScriptSerializer also makes it difficult to view the text using notepad, whereas the XML version is easier to read because it's formatted. I'll update the original question with the options, the given solutions along with pros and cons. – zumalifeguard Nov 19 '14 at 21:05
3

The simplest way to write Dictionary is to create a list of where every entry of the Dictionary is converted to an XElement. Then you create a root XElement where the list is the value of the root. The reason you want to use an XElement is because then you can use it's Save method to store it to disk as XML. Example doing that in a single line (where d is the Dictionary)

new XElement("root", d.Select(kv => new XElement(kv.Key, kv.Value)))
            .Save(filename, SaveOptions.OmitDuplicateNamespaces);

To read the file into a Dictionary, use the Parse static method of XElement and pass to it the entire contents of the file, which can read with File.ReadAllText. Parse returns an XElement object, the root. You can then iterate of the Elements() of the root and convert it to a Dictionary. You can do this in a single line:

var d = XElement.Parse(File.ReadAllText(filename))
                .Elements()
                .ToDictionary(k => k.Name.ToString(), v => v.Value.ToString());

Here's a version of the above wrapped in methods:

public static void Store(IDictionary<string, string> d, string filename)
{
    new XElement("root", d.Select(kv => new XElement(kv.Key, kv.Value)))
                .Save(filename, SaveOptions.OmitDuplicateNamespaces);
}
 public static IDictionary<string, string> Retrieve(string filename)
{
    return XElement.Parse(File.ReadAllText(filename))
                   .Elements()
                   .ToDictionary(k => k.Name.ToString(), v => v.Value.ToString());
}
crthompson
  • 15,653
  • 6
  • 58
  • 80
zumalifeguard
  • 8,648
  • 5
  • 43
  • 56
  • 2
    What if the *value* of dictionary is a complex object like *user*, *employee* etc. ? – L.B Nov 19 '14 at 19:45
  • L.B not sure why you're asking that. I suppose I'll give you the canned answer that it probably isn't valuable for a complex object. – zumalifeguard Nov 19 '14 at 19:48
  • 1
    Your solution works for only basic types, If I would write a solution, I would prefer it to work with `Dictionary` – L.B Nov 19 '14 at 19:49
  • L.B, You could say that T2 must be roundtrippable to string with some standard mechanism such as ToString and Parse. I don't think DateTime would qualify. So immediately, it would work with ints, floats and bools. Another (or additional) approach is when T2, you could reflect on each member recursively looking for string/parse combination. You could also support ISerialziable, etc. But you know, now it's getting a bit complex. As far as T1 (the key), same thing -- as long as it can be converted to/from a string. 'cause these things have to end up on a disk and we're not using binary – zumalifeguard Nov 19 '14 at 19:56
  • `But you know, now it's getting a bit complex.` Therefore don't reinvent the wheel and use a serializer that works for you. – L.B Nov 19 '14 at 19:59
  • L.B my only point was in response to you suggesting to use generics T1/T2. How would you write that generically? – zumalifeguard Nov 19 '14 at 20:04
  • My solution has the advantage that it doesn't require a third-party library such as JSON.NET. Although the accepted answer also has shows a solution that doesn't require a third partly library, the resulting output is not nicely formatted and human readable. This solution doesn't required a 3rd party library *and* the output is in format that's readable. – zumalifeguard Jan 21 '15 at 16:52
  • L.B, not reinventing the wheel. Everything you're saying about how it's better that it would be generic is correct and one would not use this technique if that's what was needed. I'd like to remind you what the original question was. The person wants to write a "string" dictionary to disk. Presumably, he already has such a dictionary and wants to know what is the easiest and best way to persist it. The right tool for the right job. If all you have is strings (which is quite common) you can use this technique. – zumalifeguard Jan 21 '15 at 16:53
  • When you use new XElement(kv.Key, kv.Value) you must obey XML naming rules for tags. For example you cannot use kv.Key which starts with digit. So you cannot save dictionary with arbitrary keys, they all must comply with XML naming rule for tags. – Max Jacobi Mar 16 '21 at 19:18
1

An old thread, but I want to add an easy answer. For .net 5 and .netCore 3.0 and above you can simply use JsonSerializer. You just need your key and value to be serializable.

Write:

using System.Text.Json;
File.WriteAllText(fileName, JsonSerializer.Serialize(dictionary));

And to read:

JsonSerializer.Deserialize<Dictionary<string, string>>(fileName);
IdoZ
  • 63
  • 3