Reputation: 362
I have multiple methods which reads data from XML having the following format:
<?xml version = "1.0" encoding="UTF-8" standalone="yes"?>
<document>
<employee>
<name>
<lastname>Sample</lastname>
<firstname>Test</firstname>
</name>
<professionalDetails>
<hiredate>October 15, 2016</hiredate>
<designation>Clerk</designation>
</professionalDetails>
<projects>
<project>
<product>Printer</product>
<id>111</id>
<price>$111.00</price>
</project>
<project>
<product>Laptop</product>
<id>222</id>
<price>$989.00</price>
</project>
</projects>
</employee>
</document>
To read the above data, I have the following methods with their respective classes.
Methods:
private static NameDetails GetsNameDetails(XNode content)
{
var element = content.XPathSelectElement("document/employee/name");
return new NameDetails
{
FirstName = element.GetElementAsString("firstName"),
LastName = element.GetElementAsString("lastName")
};
}
private static ProfessionalDetails GetsProfessionalDetailsDetails(XNode content)
{
var element = content.XPathSelectElement("document/employee/professionalDetails");
return new ProfessionalDetails
{
HireDate = element.GetElementAsString("hiredate"),
Designation = element.GetElementAsString("designation")
};
}
private static Projects GetsProjectDetails(XNode content)
{
var element = content.XPathSelectElement("document/employee/projects/project");
return new Projects
{
Id = element.GetElementAsString("id"),
Price = element.GetElementAsString("price"),
Product = element.GetElementAsString("product")
};
}
}
internal class Projects
{
public int Id { get; set; }
public string Product { get; set; }
public string Price { get; set; }
}
internal class ProfessionalDetails
{
public DateTime HireDate { get; set; }
public string Designation { get; set; }
}
internal class NameDetails
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
The underlying logic is the same for both methods. The initializing type is the only thing that gets changed. The return value of the method and the properties/fields to be initialized change, but the parameter remains the same.
How do I have one single Generic method for the below methods and decide the type to be initialised at run-time?
Upvotes: 0
Views: 65
Reputation: 3542
You could create an attribute which determines which XPath expression should be used. Example:
[XPathSelector("document/employee/projects/project")]
internal class Projects
{
public int Id { get; set; }
public string Product { get; set; }
public string Price { get; set; }
}
and then you could use this via reflection (code is untested, no c# compiler available here):
private static T GetNode<T>(XNode content)
: where T : new()
{
var selector = typeof(T).GetCustomProperty<XPathSelector>();
var element = content.XPathSelectElement(selector.XPathSelector);
var ret = new T();
foreach (var child in element.Elements) {
var property = typeof(T).GetProperties().Single(p => p.Name.ToLower() == child.Name.ToLower());
property.SetValue(ret, child.GetElementAsString(child.Name));
}
return ret;
}
Upvotes: 0
Reputation: 27367
You could write something like this:
private static T ThingFiller<T>(
XNode context,
string elementStr,
params Action<Func<string, string>, T>[] setters
)
where T : new()
{
var element = context.XPathSelectElement(elementStr);
var t = new T();
foreach (var setter in setters)
setter(element.GetElementAsString, t);
return t;
}
private static NameDetails GetsNameDetails(XNode content)
{
return ThingFiller<NameDetails>(content, "document/employee/name",
(func, nd) => nd.FirstName = func("firstName"),
(func, nd) => nd.LastName = func("lastName")
);
}
Alternatively, you could use an XML library and simply define how to fill the fields via attributes and mapping.
Upvotes: 1
Reputation: 28747
You could add a generic type and then depending on the type decide how you want to parse the node:
private static T GetsDetails<T>(XNode content)
{
if(typeof(T) == typeof(ProjectDetails)){
var element = content.XPathSelectElement("document/employee/projects/project");
return (T)new Projects
{
Id = element.GetElementAsString("id"),
Price = element.GetElementAsString("price"),
Product = element.GetElementAsString("product")
};
}
else if(typeof(T) == typeof(ProfessionalDetails){ ... }
else if (...) {...}
}
I wouldn't recommend this approach though: you still have to do type-checking and you gain very little. The initial implementation you have is far simpler.
Upvotes: 0