How to Xml-Serialize a Dictionary

How to XML-serialize a dictionary

Take a look at the following blog post

  • http://blogs.msdn.com/b/psheill/archive/2005/04/09/406823.aspx
  • http://web.archive.org/web/20100703052446/http://blogs.msdn.com/b/psheill/archive/2005/04/09/406823.aspx

and this one (not in english, but the code is useful)

  • http://huseyint.com/2007/12/xml-serializable-generic-dictionary-tipi/

Code sample from: http://web.archive.org/web/20100703052446/http://blogs.msdn.com/b/psheill/archive/2005/04/09/406823.aspx

using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using System;
public static void Serialize(TextWriter writer, IDictionary dictionary)
{
List<Entry> entries = new List<Entry>(dictionary.Count);
foreach (object key in dictionary.Keys)
{
entries.Add(new Entry(key, dictionary[key]));
}
XmlSerializer serializer = new XmlSerializer(typeof(List<Entry>));
serializer.Serialize(writer, entries);
}
public static void Deserialize(TextReader reader, IDictionary dictionary)
{
dictionary.Clear();
XmlSerializer serializer = new XmlSerializer(typeof(List<Entry>));
List<Entry> list = (List<Entry>)serializer.Deserialize(reader);
foreach (Entry entry in list)
{
dictionary[entry.Key] = entry.Value;
}
}
public class Entry
{
public object Key;
public object Value;
public Entry()
{
}

public Entry(object key, object value)
{
Key = key;
Value = value;
}
}

It generates output like the following, when the keys and values are strings.

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfEntry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Entry>
<Key xsi:type="xsd:string">MyKey</Key>
<Value xsi:type="xsd:string">MyValue</Value>
</Entry>
<Entry>
<Key xsi:type="xsd:string">MyOtherKey</Key>
<Value xsi:type="xsd:string">MyOtherValue</Value>
</Entry>
</ArrayOfEntry>

Serialize dictionary to XML C#

As UserSessionLookupTable is non-static object so its lifespan is with the life span of parent. As long you are using same instance of UserSessionCapturePlugin for all users, this will hold record of all the users.

If you are creating different instance of UserSessionLookupTable for each request that will hold only record of last user.

Also XmlSerializer can't serialize Dictionary directly.

To correct the behaviour and keep record of all the user sessions, I suggest to modify the way you are saving session session info.

  • Before saving new user session info, first load and deserialize existing xml, add new record in it, serialize again and save to file. (You need to make sure if that is first time, file will not exists so handle that)
  • Before removing user session info, first load and deserialize existing xml, remove record that you want to remove, serialize again and save it back to file.

Here is some snippet

Dictionary<Guid, UserSessionInfo> LoadUserSessionData()
{
try
{
var serializer = new XmlSerializer(typeof(KeyValuePair<Guid, UserSessionInfo>[]));

using (var stream = new FileStream(@"UserSessionLookupDictionarySerialized.xml", FileMode.Open))
{
var sessionData = (KeyValuePair<Guid, UserSessionInfo>[])serializer.Deserialize(stream)
return sessionData.ToDictionary(i => i.Key, i => i.Value);
}
}
catch (FileNotFoundException)
{
return new Dictionary<int, UserSessionInfo>();
}
}

void SaveUserSessionData(Dictionary<Guid, UserSessionInfo> sessionData)
{
var serializer = new XmlSerializer(typeof(KeyValuePair<Guid, UserSessionInfo>[]));

using (var stream = new FileStream(@"UserSessionLookupDictionarySerialized.xml", FileMode. OpenOrCreate))
{
serializer.Serialize(stream, sessionData.ToArray());
}
}

After that OnSessionChange will looks like this

public void OnSessionChange(SessionChangeDescription changeDescription)
{
switch (changeDescription.Reason)
{
//Case of Logon
case SessionChangeReason.SessionLogon:
//CreateRunningProcessesLog("UserSession-SessionLogon");

UserSession userSessionLogin = new UserSession()
{
UserName = MachineHelper.GetUsername(),
UserGuid = MachineHelper.GetUserGuid(),
MachineGuid = MachineHelper.GetMachineGUID(),
LoginTime = DateTime.Now.ToUniversalTime(),
SessionGuid = Guid.NewGuid(), //New Guid generated for tracking the UserSession, this will be created on on logon
IsReadable = false,
SessionId = changeDescription.SessionId,
};

UserSessionInfo userSessionInfoLogin = new UserSessionInfo()
{
UserName = MachineHelper.GetUsername(),
SessionGuid = userSessionLogin.SessionGuid,
IsActiveUser = true,
SessionId = changeDescription.SessionId,
LoginTime = userSessionLogin.LoginTime,
State = RowState.Added,
};

var userSessionLookupTable = LoadUserSessionData();
userSessionLookupTable.Add(userSessionInfoLogin.SessionId, userSessionInfoLogin);
SaveUserSessionData(userSessionLookupTable);
break;

//Case of Logoff
case SessionChangeReason.SessionLogoff:
UserSession userSessionLogoff = new UserSession()
{
UserName = MachineHelper.GetUsername(),
UserGuid = MachineHelper.GetUserGuid(),
MachineGuid = MachineHelper.GetMachineGUID(),
LogOffTime = DateTime.Now.ToUniversalTime(),
IsReadable = true,
SessionId = changeDescription.SessionId,
};

var userSessionLookupTable = LoadUserSessionData();
userSessionLookupTable.Remove(userSessionLogoff.SessionId);
SaveUserSessionData(userSessionLookupTable);
break;
}
}

Serialize Dictionarystring,string member to XML elements and data

Assuming that your dictionary value are all simple types that can be converted to a string, you can create your own IXmlSerializable dictionary wrapper to store and retrieve the keys and values:

public class XmlKeyTextValueListWrapper<TValue> : CollectionWrapper<KeyValuePair<string, TValue>>, IXmlSerializable
{
public XmlKeyTextValueListWrapper() : base(new List<KeyValuePair<string, TValue>>()) { } // For deserialization.

public XmlKeyTextValueListWrapper(ICollection<KeyValuePair<string, TValue>> baseCollection) : base(baseCollection) { }

public XmlKeyTextValueListWrapper(Func<ICollection<KeyValuePair<string, TValue>>> getCollection) : base(getCollection) {}

#region IXmlSerializable Members

public XmlSchema GetSchema()
{
return null;
}

public void ReadXml(XmlReader reader)
{
var converter = TypeDescriptor.GetConverter(typeof(TValue));
XmlKeyValueListHelper.ReadXml(reader, this, converter);
}

public void WriteXml(XmlWriter writer)
{
var converter = TypeDescriptor.GetConverter(typeof(TValue));
XmlKeyValueListHelper.WriteXml(writer, this, converter);
}

#endregion
}

public static class XmlKeyValueListHelper
{
public static void WriteXml<T>(XmlWriter writer, ICollection<KeyValuePair<string, T>> collection, TypeConverter typeConverter)
{
foreach (var pair in collection)
{
writer.WriteStartElement(XmlConvert.EncodeName(pair.Key));
writer.WriteValue(typeConverter.ConvertToInvariantString(pair.Value));
writer.WriteEndElement();
}
}

public static void ReadXml<T>(XmlReader reader, ICollection<KeyValuePair<string, T>> collection, TypeConverter typeConverter)
{
if (reader.IsEmptyElement)
{
reader.Read();
return;
}

reader.ReadStartElement(); // Advance to the first sub element of the list element.
while (reader.NodeType == XmlNodeType.Element)
{
var key = XmlConvert.DecodeName(reader.Name);
string value;
if (reader.IsEmptyElement)
{
value = string.Empty;
// Move past the end of item element
reader.Read();
}
else
{
// Read content and move past the end of item element
value = reader.ReadElementContentAsString();
}
collection.Add(new KeyValuePair<string,T>(key, (T)typeConverter.ConvertFromInvariantString(value)));
}
// Move past the end of the list element
reader.ReadEndElement();
}

public static void CopyTo<TValue>(this XmlKeyTextValueListWrapper<TValue> collection, ICollection<KeyValuePair<string, TValue>> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
if (collection == null)
dictionary.Clear();
else
{
if (collection.IsWrapperFor(dictionary)) // For efficiency
return;
var pairs = collection.ToList();
dictionary.Clear();
foreach (var item in pairs)
dictionary.Add(item);
}
}
}

public class CollectionWrapper<T> : ICollection<T>
{
readonly Func<ICollection<T>> getCollection;

public CollectionWrapper(ICollection<T> baseCollection)
{
if (baseCollection == null)
throw new ArgumentNullException();
this.getCollection = () => baseCollection;
}

public CollectionWrapper(Func<ICollection<T>> getCollection)
{
if (getCollection == null)
throw new ArgumentNullException();
this.getCollection = getCollection;
}

public bool IsWrapperFor(ICollection<T> other)
{
if (other == Collection)
return true;
var otherWrapper = other as CollectionWrapper<T>;
return otherWrapper != null && otherWrapper.IsWrapperFor(Collection);
}

ICollection<T> Collection { get { return getCollection(); } }

#region ICollection<T> Members

public void Add(T item)
{
Collection.Add(item);
}

public void Clear()
{
Collection.Clear();
}

public bool Contains(T item)
{
return Collection.Contains(item);
}

public void CopyTo(T[] array, int arrayIndex)
{
Collection.CopyTo(array, arrayIndex);
}

public int Count
{
get { return Collection.Count; }
}

public bool IsReadOnly
{
get { return Collection.IsReadOnly; }
}

public bool Remove(T item)
{
return Collection.Remove(item);
}

#endregion

#region IEnumerable<T> Members

public IEnumerator<T> GetEnumerator()
{
return Collection.GetEnumerator();
}

#endregion

#region IEnumerable Members

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

#endregion
}

And then use it like so:

[XmlRoot("products")]
public class Products
{
public Products()
{
Specifications = new Dictionary<string, string>();
}

[XmlIgnore]
[JsonProperty("specifications")] // For testing purposes, I compare Json.NET serialization before and after XML serialization. You can remove this.
public Dictionary<string, string> Specifications { get; set; }

[XmlElement("specifications")]
[JsonIgnore] // For testing purposes, I compare Json.NET serialization before and after XML serialization. You can remove this.
public XmlKeyTextValueListWrapper<string> XmlSpecifications
{
get
{
return new XmlKeyTextValueListWrapper<string>(() => this.Specifications);
}
set
{
value.CopyTo(Specifications = (Specifications ?? new Dictionary<string, string>()));
}
}
}

The fact that your dictionary values are simple types (directly convertible from and to text) makes it possible to avoid nested creations of XmlSerializer, which is more complex. See here for an example.

Why isn't there an XML-serializable dictionary in .NET?

The thing about XML Serialization is that it's not just about creating a stream of bytes. It's also about creating an XML Schema that this stream of bytes would validate against. There's no good way in XML Schema to represent a dictionary. The best you could do is to show that there's a unique key.

You can always create your own wrapper, for instance One Way to Serialize Dictionaries.

c# serialize dictionary into xml file

You need to convert each item to an object that is not a KeyValuePair. I use a simple class like this to serialize a Dictionary, which could easliy be modified to support a ConcurrentDictionay or ObservableCollection.

static public class XmlDictionarySerializer<A, B>
{
public class Item
{
public A Key { get; set; }
public B Value { get; set; }
}

static public void Serialize(IDictionary<A, B> dictionary, string filePath)
{
List<Item> itemList = new List<Item>();
foreach (A key in dictionary.Keys)
{
itemList.Add(new Item() { Key = key, Value = dictionary[key] });
}

XmlDataSerializer.Serialize<List<Item>>(itemList, filePath);
}

static public Dictionary<A, B> DeserializeDictionary(string filePath)
{
Dictionary<A, B> dictionary = new Dictionary<A, B>();
List<Item> itemList = XmlDataSerializer.Deserialize<List<Item>>(filePath);
foreach (Item item in itemList)
{
dictionary.Add(item.Key, item.Value);
}
return dictionary;
}
}

How to serialize/de-serialize a Dictionarystring, object into XML

You could use DataContractSerializer as explained in this post but you may have to pass the known types as a parameter to the serializer to support nested object typed classes:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.IO;
using System.Xml;

public static class SerializationExtensions
{
public static string Serialize<T>(this T obj, IEnumerable<Type> knownTypes)
{
var serializer = new DataContractSerializer(obj.GetType(), knownTypes);
using (var writer = new StringWriter())
using (var stm = new XmlTextWriter(writer))
{
serializer.WriteObject(stm, obj);
return writer.ToString();
}
}
public static T Deserialize<T>(this string serialized, IEnumerable<Type> knownTypes)
{
var serializer = new DataContractSerializer(typeof(T), knownTypes);
using (var reader = new StringReader(serialized))
using (var stm = new XmlTextReader(reader))
{
return (T)serializer.ReadObject(stm);
}
}
}

public class Address
{
public string Country { get; set; }
public string City { get; set; }
}
public class CodedAddress
{
public int CountryCode { get; set; }
public int CityCode { get; set; }
}
class Program
{
static void Main(string[] args)
{
var persons1 = new Dictionary<string, Address>();
persons1.Add("John Smith", new Address { Country = "US", City = "New York" });
persons1.Add("Jean Martin", new Address { Country = "France", City = "Paris" });

// no need to provide known types to the serializer
var serializedPersons1 = persons1.Serialize(null);
var deserializedPersons1 = serializedPersons1.Deserialize<Dictionary<string, Address>>(null);

var persons2 = new Dictionary<string, object>();
persons2.Add("John Smith", new Address { Country = "US", City = "New York" });
persons2.Add("Jean Martin", new CodedAddress { CountryCode = 33, CityCode = 75 });

// must provide known types to the serializer
var knownTypes = new List<Type> { typeof(Address), typeof(CodedAddress) };
var serializedPersons2 = persons2.Serialize(knownTypes);
var deserializedPersons2 = serializedPersons2.Deserialize<Dictionary<string, object>>(knownTypes);
}
}

How to serialize/deserialize to `Dictionaryint, string` from custom XML not using XElement?

With the help of a temporary item class

public class item
{
[XmlAttribute]
public int id;
[XmlAttribute]
public string value;
}

Sample Dictionary:

Dictionary<int, string> dict = new Dictionary<int, string>()
{
{1,"one"}, {2,"two"}
};

.

XmlSerializer serializer = new XmlSerializer(typeof(item[]), 
new XmlRootAttribute() { ElementName = "items" });

Serialization

serializer.Serialize(stream, 
dict.Select(kv=>new item(){id = kv.Key,value=kv.Value}).ToArray() );

Deserialization

var orgDict = ((item[])serializer.Deserialize(stream))
.ToDictionary(i => i.id, i => i.value);

------------------------------------------------------------------------------

Here is how it can be done using XElement, if you change your mind.

Serialization

XElement xElem = new XElement(
"items",
dict.Select(x => new XElement("item",new XAttribute("id", x.Key),new XAttribute("value", x.Value)))
);
var xml = xElem.ToString(); //xElem.Save(...);

Deserialization

XElement xElem2 = XElement.Parse(xml); //XElement.Load(...)
var newDict = xElem2.Descendants("item")
.ToDictionary(x => (int)x.Attribute("id"), x => (string)x.Attribute("value"));

Serialize Dictionary(string, Liststring) into xml

You can use Linq to Xml to create required xml from your SampleClass object:

SampleClass sample = new SampleClass();
sample.SampleProperties = new Dictionary<string, List<string>>() {
{ "Name", new List<string>() { "Greg", "Tom" } },
{ "City", new List<string>() { "London", "Warsaw" } }
};

var result = new XElement("DataItem",
sample.SampleProperties.Select(kvp =>
new XElement(kvp.Key,
kvp.Value.Select(value => new XElement("value", value)))));
result.Save(path_to_xml);

Output:

<DataItem>
<Name>
<value>Greg</value>
<value>Tom</value>
</Name>
<City>
<value>London</value>
<value>Warsaw</value>
</City>
</DataItem>

De-serializing from xml:

SampleClass sample = new SampleClass();
sample.SampleProperties = XElement.Load(path_to_xml).Elements().ToDictionary(
e => e.Name.LocalName,
e => e.Elements().Select(v => (string)v).ToList());


Related Topics



Leave a reply



Submit