在上篇文章中我们看过了DynamicObject的基本使用,这篇文章中我们通过Dynamic来实现一个操作xml的动态类型,让我们更为方便的操作xml。
其实在前面的使用ExpandoObject的文章中我们已经,通过ExpandoObject来实现了操作Xml,并体现了动态性,但是不好的是,里面没有Linq to Xml大量Api的支持,操作起来很不方便,同时我们需要编写大量的辅助方法才行。而DynamicObject可以让我们自己决定动态运行时的操作方式,我们可以很快想到通过借助XElement来实现。是的,我的想法就是这样的。
基本实现
来看看我们的Linq to Xml的语法:
var employee = new XElement( "Employee", new XElement("Name", new XElement("FirstName","Henry"), new XElement("LastName","Cui")), new XElement("Birthday","1987-10-14") );
现在我们可以借助DynamicObject来实现一种更为直观更为简洁的语法的创建Xml的方式。
首先我们来顶一个DynamicXNode类型,继承于DynamicObject:
public class DynamicXNode:DynamicObject { private XElement _XElement; #region Constructor Methods public DynamicXNode(string name) { _XElement = new XElement(name); } public DynamicXNode(XElement node) { _XElement = node; } #endregion }
我们来重写下TryGetMember 跟TrySetMember方法:
public override bool TryGetMember(GetMemberBinder binder, out object result) { var node = _XElement.Element(binder.Name); if (node != null) { result = new DynamicXNode(node); return true; } else { result = null; return false; } } public override bool TrySetMember(SetMemberBinder binder, object value) { var node = _XElement.Element(binder.Name); if (node != null) { node.SetValue(value); } else { //是否是复合的类型 if (value is DynamicXNode) { _XElement.Add(new XElement(binder.Name)); } else { _XElement.Add(new XElement(binder.Name, value.ToString())); } } return true; }
我们来写测试的代码看:
[TestMethod()] public void DynamicXNodesTest() { dynamic employee = new DynamicXNode("Employee"); employee.Name = new DynamicXNode("Name"); employee.Name.FirstName = "Henry"; employee.Name.LastName = "Cui"; employee.Birthday = "1987-10-14"; }
哦,我们还需要借助XElement来把它输出来,在重写下TryConvert方法吧:
public override bool TryConvert(ConvertBinder binder, out object result) { if (binder.Type.Equals(typeof(XElement))) { result = _XElement; return true; } return base.TryConvert(binder, out result); }
之后我们输出来可以看到:
添加额外的东西
好像已经差不多了,可是我们这里还没有Api的支持啊,我们可以将动态的方法的Invoke通过XElement去Invoke,重写
TryInvokeMember,通过反射机制到XElement上执行吧:
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { var ttype = typeof(XElement); try { result = ttype.InvokeMember(binder.Name, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, _XElement, args); return true; } catch { result = null; return false; } }