Here's a class I created to parse XML to create an IList. It seems to me that this is something that will be done a lot in general by a lot of different programs. Is there a more standard way of doing this using .Net and C#
/// <summary>
/// public class CListFromXml {}
/// Created By: Michael Fitzpatrick, 2012 Sept 25
/// Please attribute & send email: samplecode AT mikefitz dot us
/// Create an IList from XML data
/// XML Syntax -
/// <XmlNode Type="MyType" Enabled="(bool)" Iterations="(number)">
/// (data definition)
/// </XmlNode>
///
/// Type: System.Double by default
/// Type can be Enum using the following syntax:
/// Type="NameSpace.ClassParent.Class+EnumName, ClassParent"
/// See: http://stackoverflow.com/questions/2493215/create-list-of-variable-type
///
/// Enabled: true by default, If false: no list is created
///
/// Iterations: 1 by default, If 0: no list is created.
/// If LT 0 then Iterations = Int32.MaxValue
///
/// (data definition): As CSV list-
/// <CsvString> (item), (item), ... </CsvString>
/// where:
/// (item) is any value. For arrays item is bracketed using "{}"
/// just like C# uses for initializers. Example:
/// <MeasProp Type="System.Double[]">
/// {1960, 1970, 1980},
/// {2960, 2970, 2980},
/// {2960}
/// </MeasProp>
///
/// (data definition): As Center / StepDelta -
/// <LoopSpec>
/// <Center>(some value)</Center>
/// <StepDelta>(some value)</StepDelta> <!-- OPTIONAL -->
/// <StepsUp>(some integer)</StepsUp> <!-- OPTIONAL -->
/// <StepsDown>(some integer)</StepsDown> <!-- OPTIONAL -->
/// </LoopSpec>
/// </summary>
public class CListFromXml
{
public readonly IList Data = null;
public readonly string Name;
public readonly bool Enabled;
public readonly Type Type;
public readonly string TypeName;
public readonly Int32 Iterations;
private static bool ReadCsv(Type typ, string csvData, IList lst)
{
string[] astr = csvData
.Split(new[] { ",", "\r\n", "\n", " ", "\t", "}" }
, StringSplitOptions.RemoveEmptyEntries);
if (astr.Length == 0)
return false;
if (typ == typeof(bool))
foreach (string s in astr)
lst.Add(CUtils.BoolParse(s));
else if (typ.IsEnum)
foreach (string s in astr)
lst.Add(Enum.Parse(typ, s));
else
{
try
{
foreach (string s in astr)
{
object o = Convert.ChangeType(s, typ);
lst.Add(o);
}
}
catch
{
throw new NotSupportedException("CListFromXml: Type not supported:"
+ typ.ToString());
}
}
return true;
}
// ============================================================
/// <summary>
/// Create a list from XML data
/// </summary>
/// <param name="n"></param>
public CListFromXml(XmlNode n)
{
if (!(Enabled = CUtils.IsNodeEnabled(n)))
return;
if ((Iterations = CUtils.NodeIterations(n)) == 0)
return;
Name = n.Name;
TypeName = CUtils.AttribValue(n, "type");
if (TypeName != "")
{
Type = Type.GetType(TypeName);
if (Type == null)
throw new InvalidDataException("Invalid XML File: "
+ " the element " + Name
+ " has an invalid type attribute: " + TypeName);
}
else
Type = typeof(double);
Type lstType = typeof(List<>).MakeGenericType(Type);
Data = (IList)Activator.CreateInstance(lstType);
if (n.FirstChild.ChildNodes.Count == 0)
{
if (Type.IsArray)
{
// String format for Arrays is { <item>, <item>, ...} , { ...
string[] astr = n.InnerText.Trim()
.Split(new[] { "{"}
, StringSplitOptions.RemoveEmptyEntries);
if (astr.Length > 0)
{
IList lst;
Type aryType;
try
{
aryType = Type.GetElementType();
Type lstType1 = typeof(List<>).MakeGenericType(aryType);
lst = (IList)Activator.CreateInstance(lstType1);
}
catch (Exception e)
{
throw new NotSupportedException("CListFromXml: XML Node: "
+ Name + ", Type not supported:"
+ TypeName + "\r\n" + e.ToString());
}
//Type t1 = Data.GetType().GetGenericArguments()[0];
foreach (string s in astr)
{
if (!ReadCsv(aryType, s, lst ))
throw new InvalidDataException("Invalid XML File"
+ " the element " + Name
+ " has an invalid value: " + n.InnerText
+ "\nExpected comma delimited list of numbers.");
Array ary = (Array)Activator.CreateInstance(Type, new object[] { lst.Count });
lst.CopyTo(ary,0);
Data.Add(ary);
lst.Clear();
}
}
}
else
{
// Process as a csv list
if (!ReadCsv(Type, n.InnerText, Data))
throw new InvalidDataException("Invalid XML File"
+ " the element " + Name
+ " has an invalid value: " + n.InnerText
+ "\nExpected comma delimited list of numbers.");
}
}
else
{
if (!CUtils.IsTypeNumeric(Type))
throw new Exception("Error in XML file: '"
+ "', In element: " + Name
+ ", Only numeric types are supported for 'Center / Delta' lists");
ValueType center = CInvalid.Dbl, stepDelta = 0;
Int32 stepsUp = 0, stepsDown = 0;
foreach (XmlNode m in n.ChildNodes)
{
switch (m.Name.ToLower())
{
case "center":
center = (ValueType)Convert.ChangeType(
m.InnerText.Trim(), Type);
break;
case "stepdelta":
stepDelta = (ValueType)Convert.ChangeType(
m.InnerText.Trim(), Type);
break;
case "stepsup":
stepsUp = Int32.Parse(m.InnerText.Trim());
break;
case "stepsdown":
stepsDown = Int32.Parse(m.InnerText.Trim());
break;
case "#comment":
break;
default:
throw new Exception("Error in XML file: '"
+ "', In element: " + Name
+ ", invalid element: " + m.Name);
}
}
Data.Add(center);
if (!stepDelta.Equals(0) && ((stepsDown > 0) || (stepsUp > 0)))
{
double dC = (double)Convert.ChangeType(center, typeof(double));
double dD = (double)Convert.ChangeType(stepDelta, typeof(double));
for (Int32 i = 1; i <= stepsDown; i++)
{
double d = dC - (i * dD);
Data.Add(Convert.ChangeType(d, Type));
}
for (Int32 i = 1; i <= stepsUp; i++)
{
double d = dC + (i * dD);
Data.Add(Convert.ChangeType(d, Type));
}
}
}
} // CListFromXml(IList lst, XmlNode n)
} // CListFromXml{}