Helper method for creating a generic class dictionary in C #
I have an application where I have several classes that I got from XML files (The property names in the class correspond to the node names in XML) and I can populate this class using reflection (some code I found on stackoverflow which I adapted). Basically I am listing all the child XML node and trying to get and set the corresponding property name in my class. This works well enough. While I understand that XML Serialization might be the best solution for this, I am fine with what I have. My question is what I am doing with populated classes.
Reading the XML, I would (say) get 200 nodes that convert to 200 instances of this class. I want to store them in a dictionary. Then I would add another XML to (say) create 100 instances of another class, and I want them to be stored in another dictionary.
The code I am using to parse the XML looks like this:
Dictionary<string, Book> oBookCollection = new Dictionary<string, Book>();
XML_File_Reader oDoc = new XML_File_Reader(@"C:\apps\bookList.xml");
if (oDoc.IsDocumentValid)
{
XmlNodeList oList = oDoc.GetList("Row");
if (oList.Count !=0)
foreach (XmlNode xn in oList)
{
XmlNode oThisNode = xn;
Book oThisBook = new Book(oMyLibrary, oThisNode);
oDoc.MigrateXMLtoObject(oBuilding, oThisNode);
oBookCollection .Add(oBook.BookName, oBook);
}
}
oDoc = null;
As you can see, this has specific references to a specific class type. I want to be able to use a generic method that can populate XML for DVDs and toys as easily as for books.
For information, the existing procedure "MigrateXMLtoObject" looks like this:
public void MigrateXMLtoObject(object myObject, XmlNode node)
{
if (node == null)
{
return;
}
PropertyInfo propertyInfo;
IEnumerator list = node.GetEnumerator();
XmlNode tempNode;
while (list.MoveNext())
{
tempNode = (XmlNode)list.Current;
propertyInfo = myObject.GetType().GetProperty(tempNode.Name);
if (propertyInfo != null)
{
if (propertyInfo.PropertyType == typeof(DateTime))
{
DateTime val = DateTime.MinValue;
DateTime.TryParse(tempNode.InnerText, out val);
propertyInfo.SetValue(myObject, val, null);
}
else if (propertyInfo.PropertyType == typeof(Int32))
{
int val = 0;
Int32.TryParse(tempNode.InnerText, out val);
propertyInfo.SetValue(myObject, val, null);
}
else if (propertyInfo.PropertyType == typeof(bool))
{
bool val = false;
Boolean.TryParse(tempNode.InnerText, out val);
propertyInfo.SetValue(myObject, val, null);
}
else if (propertyInfo.PropertyType == typeof(string))
{
string val = "";
val = tempNode.InnerText;
propertyInfo.SetValue(myObject, val, null);
}
else
{
propertyInfo.SetValue(myObject, tempNode.Value, null);
}
}
}
}
What I have reviewed is making books, DVDs, etc. inheriting from a common base class, but I don't think it will work because if I pass the BaseClass to a method when it tries to use reflection to match a property, they will be properties of the base layer, not the derived class.
Then I did some searching here and saw mention of using Generics. Unfortunately, although I use dictionary <> and list <> objects, I have never written my own Generic and therefore do not know how to use them (and this is not actually the solution I am looking for!)
I wrote this and it's clear that it won't work / compile, but this might be a useful snippet for people to nudge me in the right direction:
public Dictionary<string, T> MigrateXMLtoObject<T>(string xmlPath, MyLibrary oLibrary)
{
Dictionary<string, T> oDictionary = new Dictionary<string, T>();
XML_File_Reader oDoc = new XML_File_Reader(xmlPath);
if (oDoc.IsDocumentValid)
{
XmlNodeList oList = oDoc.GetList("Row");
if (oList.Count != 0)
foreach (XmlNode xn in oList)
{
XmlNode oThisNode = xn;
//T othisItem = new T(oLibrary, oThisNode);
oDoc.MigrateXMLtoObject(T, oThisNode);
oDictionary.Add(T.Type, T);
}
}
oDoc = null;
return oDictionary;
}
Do I need some kind of "switch" to evaluate T and create an appropriate base class?
Thank!
source to share
Without additional context, it's hard to know exactly what's best for you. But naively, here's what I suggest:
public Dictionary<string, T> MigrateXMLtoObject<T>(
string xmlPath, MyLibrary oLibrary, Func<MyLibrary, XmlNode, T> factory)
{
Dictionary<string, T> oDictionary = new Dictionary<string, T>();
XML_File_Reader oDoc = new XML_File_Reader(xmlPath);
if (oDoc.IsDocumentValid)
{
XmlNodeList oList = oDoc.GetList("Row");
if (oList.Count != 0)
foreach (XmlNode xn in oList)
{
XmlNode oThisNode = xn;
T thisItem = factory(oLibrary, oThisNode);
// It not really clear to me what you wanted to do
// here. If the object constructor is handling applying
// the node data to the object, you shouldn't need to
// "migrate" more, right? And the dictionary seems to
// want to add the object as the value, with the key
// being some text. Where do you get the key?
//oDoc.MigrateXMLtoObject(T, oThisNode);
oDictionary.Add(objectKey, thisItem);
}
}
oDoc = null;
return oDictionary;
}
If you call it something like this:
MigrateXMLtoObject(xmlPath, library, (l, n) => new Book(l, n));
Note that if you are passing an object MyLibrary
only to pass to the object's constructor, then in this approach you don't need it as a method parameter. You can create it in a factory delegate instead. For example:.
MigrateXMLtoObject(xmlPath, node => new Book(library, n));
I also suggest that you get rid of all those "o's" prefixed with variable names. It does not help.:)
source to share
What I've looked at is doing inheritance for books, DVDs, etc. generic base class, but don't think it will work because if I pass the BaseClass to a method when it tries to use reflection to match a property, they will be properties of the base layer, not the derived class.
sorry, but you think it's wrong :( ..
public void SayMyType(BaseClass obj)
{
Console.Write(obj.GetType())//will output the type of object 'DerivedClass' not the parameter 'BaseClass '
}
SayMyType(new DerivedClass())
so inheritance is the way to go.
source to share