views:

316

answers:

1

I have a list of key/value pairs I'd like to store in and retrieve from a XML file. So this task is similar as described here. I am trying to follow the advice in the marked answer (using a KeyValuePair and a XmlSerializer) but I don't get it working.

What I have so far is a "Settings" class ...

public class Settings
{
    public int simpleValue;
    public List<KeyValuePair<string, int>> list;
}

... an instance of this class ...

Settings aSettings = new Settings();

aSettings.simpleValue = 2;

aSettings.list = new List<KeyValuePair<string, int>>();
aSettings.list.Add(new KeyValuePair<string, int>("m1", 1));
aSettings.list.Add(new KeyValuePair<string, int>("m2", 2));

... and the following code to write that instance to a XML file:

XmlSerializer serializer = new XmlSerializer(typeof(Settings));
TextWriter writer = new StreamWriter("c:\\testfile.xml");
serializer.Serialize(writer, aSettings);
writer.Close();

The resulting file is:

<?xml version="1.0" encoding="utf-8"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
  <simpleValue>2</simpleValue>
  <list>
    <KeyValuePairOfStringInt32 />
    <KeyValuePairOfStringInt32 />
  </list>
</Settings>

So neither key nor value of the pairs in my list are stored though the number of elements is correct. Obviously I am doing something basically wrong. My questions are:

  • How can I store the key/value pairs of the list in the file?
  • How can I change the default generated name "KeyValuePairOfStringInt32" of the elements in the list to some other name like "listElement" I'd like to have?
+3  A: 

KeyValuePair is not serializable, because it has read-only properties. Here is more information(thanks to Thomas Levesque). For changing the generated name use the [XmlType] attribute.

Define your own like this:

[Serializable]
[XmlType(TypeName="WhateverNameYouLike")]
public struct KeyValuePair<K, V>
{
  public K Key 
  { get; set; }

  public V Value 
  { get; set; }
}
Petar Minchev
Great, it works, thank you! I've added a constructor to your struct: `public KeyValuePair(K k, V v) : this() { Key = k; Value = v; }`, so I can use my code to fill the list without changes. Also I figured out that as an alternative to `[XmlType]` I can decorate the list in the Settings class with `[XmlArrayItem(ElementName="WhateverNameYouLike")]`. It can be useful if I had two lists of `KeyValuePair` but want to give the elements different names in the XML file. Thanks again!
Slauma
Glad to help you:)
Petar Minchev
It has nothing to do with the `Serializable` attribute : XML serialization doesn't need it. The reason is that the `Key` and `Value` properties are read-only
Thomas Levesque
@Thomas, Wow thanks. You are right - http://blogs.msdn.com/seshadripv/archive/2005/11/02/488273.aspx
Petar Minchev
@Thomas: Thanks for explanation! Yes, I've removed the `[Serializable]` attribute and it still works. Does that generally mean that only read/write properties (and public members?) can be Xml-serialized? Edit: Petar's link already answered the question :)
Slauma
Just to mention: The `[XmlType]` attribute applied to the class template turned out to be a problem when I have two lists of different types of KeyValuePair in my Settings class, for instance a `List<KeyValuePair<string, int>>` and a `List<KeyValuePair<int, int>>`. Then the constructor of XmlSerializer throws an exception and complains that I have the same XML-TypeName for two different types which seems to be forbidden. For now I have removed the XmlType attribute and applied `[XmlArrayItem...]` directly to the list members as mentioned in my first comment.
Slauma