I am successfully using XML serializer to serialize a Microsoft.VisualStudio.Services.Common.SerializableDictionary<T1, T2>.
Now I want to add a guid string to SerializableDictionary and serialize it as well. I therefore created the following class:
public class SerializableDictWithGuid<T1, T2> : SerializableDictionary<T1, T2>, IMyInterface
{
[XmlElement(ElementName = idXml001)]
public string Guid { get; set; } = string.Empty;
}
When serializing SerializableDictWithGuid with the below code, the guid string is missing in the XML output.
public class XmlSerializeHelper<T> where T : class
{
public static string Serialize(T obj)
{
try
{
System.Xml.Serialization.XmlSerializer xsSubmit = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (var sww = new StringWriter())
{
using (XmlTextWriter writer = new XmlTextWriter(sww) { Formatting = Formatting.Indented })
{
xsSubmit.Serialize(writer, obj);
}
return sww.ToString();
}
}
catch (Exception ex)
{
logger.log(ex);
}
return string.Empty;
}
}
Minimal reproducible example
SerializableDictWithGuid <string, string> test = new SerializableDictWithGuid <string, string>() { Guid = "123" };
XmlSerializeHelper<SerializableDictWithGuid <string, string>>.Serialize(test);
Question: how can I add properties to SerializableDictionary and include them in XML serialization?
XmlSerializer
does not support dictionaries, so SerializableDictionary<T1, T2>
works by implementing IXmlSerializable
. I.e., it serializes itself manually using handcrafted code. As such, that code knows nothing about the properties of subclasses, and doesn't have any code to discover and serialize them automatically.
Luckily it appears that IXmlSerializable.ReadXml()
and IXmlSerializable.WriteXml()
were not implemented explicitly, so it should be possible to re-implement them via interface re-implementation and still call the base class methods. And, while in general it's tricky to implement IXmlSerializable
, if you make your Guid
be an XML attribute rather than an element, implementation becomes simple because, while the design of IXmlSerializable
doesn't allow for derived classes to inject child elements, it does allow for derived classes to inject custom attributes:
public class SerializableDictWithGuid<T1, T2> : SerializableDictionary<T1, T2>, IMyInterface, IXmlSerializable // Re-implement IXmlSerializable
{
const string idXml001 = "idXml001";
public string Guid { get; set; } = string.Empty;
public new void ReadXml(XmlReader reader)
{
reader.MoveToContent();
// At this point the reader is positioned on the beginning of the container element for the dictionary, so we can get any custom attributes we wrote.
Guid = reader.GetAttribute(idXml001);
base.ReadXml(reader);
}
public new void WriteXml(XmlWriter writer)
{
// At this point the container element has been written but nothing else, so it's still possible to add some attributes.
if (Guid != null)
writer.WriteAttributeString(idXml001, Guid);
base.WriteXml(writer);
}
}
This results in XML that looks like:
<SerializableDictWithGuidOfStringString idXml001="123">
<item>
<key>
<string>hello</string>
</key>
<value>
<string>there</string>
</value>
</item>
</SerializableDictWithGuidOfStringString>
The XML Standard states that
Attributes are used to associate name-value pairs with elements.
In general attributes are appropriate for fixed sets of simple, primitive values such as names and IDs. Complex values with multiple internal properties, or values that repeat (i.e. collections) should be child elements, not attributes. Since an identifying Guid is a primitive value, using an attribute is appropriate. See:
Demo fiddle here.