For some time I have been working on and off on an attempt to create a useful, powerful, and easy to use library to help with serializing and deserializing data instances to and from XML. Without repeating too much, the core concept is to have an IXmlPersistable interface implemented by classes, or to have an IXmlPersistableProvider implementation made available that the library can associate with that type to save/load instances to and from an XElement node.
For the most part, it has gone quite well. However I hit some interesting snags with regards to Generic types, particularly, Generic types that I want to write IXmlProvider implementations for. For example, let’s say we want to support serializing and deserializing a List<T>. We obviously cannot implement every single permutation of IXmlProvider<List<T>>, since implementing an interface requires a concrete class definition. What this means is that if I wanted to support saving/reading a List via a PersistableProvider, I would need an implementation that implements IXmlPersistableProvider<List<String>> and so on for each possible type- which means of course that types that I don’t know about at compile time could never be included. Unfortunately the logic is too complex to embody via generics- really, I’d want an implementation of IXmlPersistableProvider<List<T>> where T was any type that itself either implemented IXmlPersistable or for which there was an available IXmlPersistableProvider<T>. So clearly I needed to find an alternative approach.
My first consideration/implementation was to not have any sort of Provider at all; instead, I created a SaveList<T> method and a corresponding ReadList<T> method, which would attempt to save each element T of the List by using the generic SaveElement<T> method I created:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public static XElement SaveList<T>(List<T> SourceData, String pNodeName) { //without a Func as in the overload, we'll try to "build" our own function. //the function we build will close over a instance of IXMLSerializationProvider (or the type GetXMLData method if it implements IXMLSerializable). //basically we want to create the function to handle the loading of classes that implement the interface or have a defined provider. Func<T, XElement> buildfunc = (elem) => SaveElement(elem, pNodeName); return SaveList<T>(buildfunc, pNodeName, SourceData); } /// <summary> /// Saves a list to an XElement. /// </summary> /// <typeparam name="T">Type of the list to save.</typeparam> /// <param name="SourceTransform">Function that takes the Type T item and saves it to an XElement.</param> /// <param name="SourceData">Source List data.</param> /// <returns></returns> public static XElement SaveList<T>(Func<T, XElement> SourceTransform, String pNodeName, List<T> SourceData) { XElement ListNode = new XElement (pNodeName, new XAttribute("ListType", typeof (T).Name)); foreach (T iterateNode in SourceData) { XElement addContent = SourceTransform(iterateNode); ListNode.Add(addContent); } return ListNode; } public static List<T> ReadList<T>(XElement Source) { Func<XElement, T> constructitem = (xdata) => ReadElement<T>(xdata); return ReadList<T>(constructitem, Source); } public static List<T> ReadList<T>(Func<XElement, T> ListLoader, XElement Source) { List<T> resultlist = new List<T>(); foreach (XElement child in Source.DescendantNodes()) { T resultnode = ListLoader(child); resultlist.Add(resultnode); } return resultlist; } |
However I soon discovered that there was a bit of a caveat to that approach. In my case I discovered it after expanding to create a similar construct for Dictionaries. If there was Dictionary where either the key or the value type was a List, than it wouldn’t work properly- as there is no implementation of the Providers or interface to save/read a List! So it was back to the drawing board. it was then that I decided that while I couldn’t implement a all-emcompassing IXmlPersistableProvider for the List<T> type, I could have an implementation that covered IList and IDictionary, which are interfaces implemented by the List and Dictionary generic types respectively. There is still a caveat in that saving a Dictionary<String,List<String>> will save properly, but calling ReadElement will instead return a Dictionary<String,IList>, but for the moment I cannot determine a reasonable method to do otherwise within the framework I have created. For now I’m thinking that can be a sort of advisory for the actual Persisting code to keep in mind if it ever needs to save/load nested Generic types.
Have something to say about this post? Comment!