I'm creating a program that allow user define formulas with on 4 basic operation: add, subtract, divide, multiple using XML. Let's take an example: User want to define formula like (a + b) x (c + d)
. The format of the xml as following:
EDIT I had implement this
EDIT Solve. Many thanks to Yaniv's suggestion. My solution as follow:
<xPlugins>
<xPlugin>
<Multiple>
<Add>
<Operator>
<value>1</value>
</Operator>
<Operator>
<value>2</value>
</Operator>
</Add>
<Add>
<Operator>
<value>3</value>
</Operator>
<Operator>
<value>4</value>
</Operator>
</Add>
</Multiple>
</xPlugin>
</xPlugins>
classes
//root element
public class xPlugins
{
[XmlElement("xPlugin", typeof(xPlugin))]
public xPlugin[] Plugin { get; set; }
}
public class xPlugin
{
[XmlElement("Multiple", typeof(Multiple))]
[XmlElement("Add", typeof(Add))]
[XmlElement("Subtract", typeof(Divide))]
[XmlElement("Divide", typeof(Divide))]
[XmlElement("Operator", typeof(Operand))]
public Calculator calculator { get; set; }
}
//Deseirialize ultility
static class readXML
{
public static void getObject(ref xPlugins plugins)
{
try
{
List<Type> type = new List<Type>();
type.Add(typeof(Add));
type.Add(typeof(Minus));
type.Add(typeof(Multiple));
type.Add(typeof(Subtract));
type.Add(typeof(Operator));
XmlSerializer xml = new XmlSerializer(typeof(xPlugin), type.ToArray());
FileStream fs = new FileStream("test.xml", FileMode.Open);
plugins = (xPlugins)xml.Deserialize(fs);
}
catch (Exception ex)
{
throw;
}
}
}
public abstract class Calculator
{
[XmlElement("Multiple", typeof(Multiple))]
[XmlElement("Add", typeof(Add))]
[XmlElement("Subtract", typeof(Subtract))]
[XmlElement("Divide", typeof(Divide))]
[XmlElement("Operator", typeof(Operand))]
public List<Calculator> calculators{ get; set; }
public virtual int Calculate()
{
return 0;
}
}
public class Operator : Calculator
{
public int value { get; set; }
public Operator() { }
public override int Calculate()
{
return value;
}
}
public class Add : Calculator
{
public Add() { }
public override int Calculate()
{
List<int> value = new List<int>();
foreach (Calculator calculator in calculators)
{
value.Add(calculator.Calculate());
}
return value.Sum();
}
}
public class Minus : Calculator
{
public Minus() { }
public override int Calculate()
{
int value = calculators[0].Calculate();
for (int i = 1; i < calculators.Count; i++)
{
value -= calculators[i].Calculate();
}
return value;
}
}
public class Divide: Calculator
{
public Divide() { }
public override int Calculate()
{
int value = calculators[0].Calculate();
for (int i = 1; i < calculators.Count; i++)
{
value /= calculators[i].Calculate();
}
return value;
}
}
public class Multiple : Calculator
{
public Multiple() { }
public override int Calculate()
{
int value = calculators[0].Calculate();
for (int i = 1; i < calculators.Count; i++)
{
value *= calculators[i].Calculate();
}
return value;
}
}
//running test
private void button1_Click(object sender, EventArgs e)
{
readXML.getObject(ref this.plugins);
foreach (Calculator plugin in plugins.calculators)
{
plugin.Calculate();
}
}
I just have to decorate Calculator
property with:
[XmlElement("Multiple", typeof(Multiple))]
[XmlElement("Add", typeof(Add))]
[XmlElement("Subtract", typeof(Divide))]
[XmlElement("Divide", typeof(Divide))]
[XmlElement("Operator", typeof(Operand))]
I am guessing you want to use XmlSerializer. If you need a "polymorphic" deserialization you can pass a list of types that the serializer should know about (this works if they all inherit from the same base class but not from interface).
Example:
List<Type> extraTypes = new List<Type>();
extraTypes.Add(typeof(multiple));
extraTypes.Add(typeof(add));
extraTypes.Add(typeof(substract));
extraTypes.Add(typeof(divide));
var ser = new XmlSerializer(typeof(Foo), extraTypes.ToArray());
It's explained here: Serializing and restoring an unknown class
But there is another problem that in your XML your operand can hold two different types: an operation or an parameter (a, b, c, d) and you cannot represent it in your class.
Something that I usually see is this (I implemented only the add operation, and I am assuming the expression is numeric):
public class Expression
{
public virtual int Evaluate()
{
}
}
public class Add : Expression
{
Expression _left;
Expression _right;
public Add(Expression left, Expression right)
{
_left = left;
_right = right;
}
override int Evalute()
{
return _left.Evalute() + _right.Evalute();
}
}
public class Parameter : Expression
{
public int Value{get;set;}
public Parameter(string name)
{
// Use the name however you need.
}
override int Evalute()
{
return Value;
}
}
This way you have only one base class so everything is simpler. If that make sense I guess it won't be hard to deserialize it.
EDIT: If the base class is Calculator (instead of Expression) the XML will look like this:
<Calculator xsi:type="Multiple">
<calculators>
<Calculator xsi:type="Add">
<calculators>
<Calculator xsi:type="Operator">
<value>12</value>
</Calculator>
</calculators>
</Calculator>
</calculators>
</Calculator>
I have created a simple calculator object and serialized it and that's what I got. If you will deserialize it you will get a calculator that will return 12.
Maybe you can use XmlAttributes to change the names of the elements in the XML or in the worst case write your own deserializer.
来源:https://stackoverflow.com/questions/17292634/deserialize-xml-into-super-class-object-with-c-sharp