您的位置:首页 > 其它

使用dynamic特性处理XML文档

2009-12-06 00:16 495 查看
处理XML文档是我们经常需要进行的一项工作,尤其是在进行网络服务相关编程时,比如更新RSS等。在.NET 3.5中引入了Linq To XML,使得XML文档的读写已经大大简化,而.NET 4.0中最新的dynamic特性,则将简化发挥到了极致。以处理白云黄鹤的“十大”为例,数据源地址为http://www.byhh.net/posttop10.xml,其当前内容为(为使结果显示清晰,去掉了其中的链接地址字段):

<?xml version="1.0" encoding="gb2312" ?>
<?xml-stylesheet type="text/xsl" href="/style/blue/xsl/posttop.xsl"?>
<toppost>
<post>
<board>WorldSoccer</board>
<title>大家支持哪队?                                             </title>
</post>
<post>
<board>HUSTStudent</board>
<title>地震了???                                               </title>
</post>
<post>
<board>Picture</board>
<title>想当二奶的有门路了!                                       </title>
</post>
<post>
<board>Picture</board>
<title>轻拍。。。不要太挑剔了                                     </title>
</post>
<post>
<board>Humor</board>
<title>【原创】几个小humor                                        </title>
</post>
<post>
<board>HUSTStudent</board>
<title>院士增选的一个疑问                                         </title>
</post>
<post>
<board>WorldSoccer</board>
<title>卧槽,1:55才开始抽??                                    </title>
</post>
<post>
<board>WorldSoccer</board>
<title>主持MM好靓                                                 </title>
</post>
<post>
<board>TrainFan</board>
<title>1000                                                       </title>
</post>
<post>
<board>Movies</board>
<title>还是推荐熊猫大侠,早上才十块钱.                             </title>
</post>
</toppost>
好,我们先不管它的链接,仅仅获取其中的版面名(board)和标题(title)。那么使用Linq To XML,处理方法如下:

var doc = XDocument.Load ("http://www.byhh.net/posttop10.xml");
foreach (var post in doc.Element ("toppost").Elements ("post"))
{
Console.WriteLine ("版面:" + post.Element ("board").Value);
Console.WriteLine ("标题:" + post.Element ("title").Value.Trim ());
}
输出结果:

版面:HUSTStudent
标题:地震了???
版面:WorldSoccer
标题:大家支持哪队?
版面:Humor
标题:【原创】几个小humor
版面:WorldSoccer
标题:2010年世界杯抽签结果
版面:HUSTStudent
标题:当武大陷入教授门、贪污门时。华中科大又多了两?
版面:Bicycle
标题:雅京行纪录片-1-雅典
版面:Picture
标题:想当二奶的有门路了!
版面:Picture
标题:轻拍。。。不要太挑剔了
版面:Humor
标题:【原创】关于找工作
版面:TrainFan
标题:1000
ress any key to continue . . .

那在.NET 4.0 中,又是怎样的呢,先看我最后的代码:

XDocument doc = XDocument.Load ("http://www.byhh.net/posttop10.xml");
dynamic posts = doc.Element ("toppost").AsDynamic ();
foreach (var post in posts.post)
{
Console.WriteLine ("版面:" + post.board);
Console.WriteLine ("标题:" + post.title.Trim ());
}
此时,我们已经完全去掉了啰嗦的Element和Elements方法以及Value属性的调用,查询post节点中的board节点、title节点,就只需要post.board和post.title即可,多么方便!而这一切的秘密,就在于dynamic关键字和那个AsDynamic方法。不过,不要以为你新建一个VS2010粘贴以上代码就能运行,“简单是以复杂为支撑的”,之所以能这么写,是因为我实现了两个帮助类XmlNode和XmlNodeList(不是System.Xml命名空间中的那个)来提供dynamic特性的支持,另外还实现了一个静态类来提供XElement上的扩展方法。具体代码如下。

class XmlNode : DynamicObject
{
private XElement _element;

public XmlNode (string name)
: this (new XElement (name))
{
}

public XmlNode (XElement element)
{
_element = element;
}

public override bool TryInvokeMember (InvokeMemberBinder binder,
object[] args, out object result)
{
string name = binder.Name;

if (String.CompareOrdinal (name, "SelectAll") == 0)
{
IEnumerable<XElement> selectedElements = null;

if (args.Length == 0)
{
selectedElements = _element.Descendants ();
}
else
{
selectedElements = _element.Descendants (args[0].ToString ());
}

result = new XmlNodeList (selectedElements);
return true;
}
else if (String.CompareOrdinal (name, "SelectChildren") == 0)
{
IEnumerable<XElement> selectedElements = null;

if (args.Length == 0)
{
selectedElements = _element.Elements ();
}
else
{
selectedElements = _element.Elements (args[0].ToString ());
}

result = new XmlNodeList (selectedElements);
return true;
}

return base.TryInvokeMember (binder, args, out result);
}

public override bool TryGetMember (GetMemberBinder binder, out object result)
{
string name = binder.Name;

if (String.CompareOrdinal (name, "Name") == 0)
{
result = _element.Name.LocalName;
return true;
}
else if (String.CompareOrdinal (name, "Parent") == 0)
{
XElement parent = _element.Parent;
if (parent != null)
{
result = new XmlNode (parent);
return true;
}
result = null;
return true;
}
else if (String.CompareOrdinal (name, "Value") == 0)
{
result = _element.Value;
return true;
}
else if (String.CompareOrdinal (name, "Nodes") == 0)
{
result = new XmlNodeList (_element.Elements ());
return true;
}
else if (String.CompareOrdinal (name, "Xml") == 0)
{
StringWriter sw = new StringWriter ();
_element.Save (sw, SaveOptions.None);

result = sw.ToString ();
return true;
}
else
{
XAttribute attribute = _element.Attribute (name);
if (attribute != null)
{
result = attribute.Value;
return true;
}

var childNodes = _element.Elements (name).ToArray ();
if (childNodes.Length == 1)
{
if (!childNodes[0].HasElements)
{
result = childNodes[0].Value;
return true;
}
result = new XmlNode (childNodes[0]);
return true;
}
else if (childNodes.Length > 1)
{
result = new XmlNodeList (childNodes);
return true;
}
}

return base.TryGetMember (binder, out result);
}

public override bool TrySetMember (SetMemberBinder binder, object value)
{
string name = binder.Name;

if (String.CompareOrdinal (name, "Value") == 0)
{
_element.Value = (value != null) ? value.ToString () : String.Empty;
}

return base.TrySetMember (binder, value);
}
}

下面是XmlNodeList,用于处理子节点列:

class XmlNodeList : DynamicObject, IEnumerable
{
private List<XElement> _elements;

internal XmlNodeList (IEnumerable<XElement> elements)
{
_elements = new List<XElement> (elements);
}

public override bool TryConvert (ConvertBinder binder, out object result)
{
Type targetType = binder.ReturnType;

if (targetType == typeof (IEnumerable))
{
result = this;
return true;
}

return base.TryConvert (binder, out result);
}

public override bool TryInvokeMember (InvokeMemberBinder binder,
object[] args, out object result)
{
if (string.CompareOrdinal (binder.Name, "Item") == 0)
{
if (args.Length == 1)
{
XElement element = _elements[Convert.ToInt32 (args[0])];
result = new XmlNode (element);
return true;
}
}

return base.TryInvokeMember (binder, args, out result);
}

public override bool TryGetMember (GetMemberBinder binder, out object result)
{
if (string.CompareOrdinal (binder.Name, "Length") == 0)
{
result = _elements.Count;
return true;
}

return base.TryGetMember (binder, out result);
}

#region Implementation of IEnumerable
IEnumerator IEnumerable.GetEnumerator ()
{
return new NodeEnumerator (_elements.GetEnumerator ());
}
#endregion

private sealed class NodeEnumerator : IEnumerator
{

private IEnumerator<XElement> _elementEnumerator;

public NodeEnumerator (IEnumerator<XElement> elementEnumerator)
{
_elementEnumerator = elementEnumerator;
}

public object Current
{
get
{
XElement element = _elementEnumerator.Current;
return new XmlNode (element);
}
}

public bool MoveNext ()
{
return _elementEnumerator.MoveNext ();
}

public void Reset ()
{
_elementEnumerator.Reset ();
}
}
}

可以看到,这两个类都继承自DynamicObject,并覆写了基类的一些方法,正因为如此,它们才能对dynamic上的调用做出特殊相应。需要注意的是,虽然我这里是写了两个类,但它们依然是与我的应用无关的,也就是说,我处理任何XML文档,都可以使用上述方法,千万不要以为我为了处理白云黄鹤的十大而特别写了这两个类。这两个类的代码参考了nikhilk的博客(http://www.nikhilk.net/CSharp-Dynamic-Programming-REST-Services.aspx)的内容,不过他的实现是基于.NET 4.0 CTP的,在beta2上已经无法使用。

最后贴上那个扩展方法AsDynamic()的实现:

static class XmlExtension
{
public static XmlNode AsDynamic (this XElement source)
{
return new XmlNode (source);
}

public static XmlNodeList AsDynamic (this IEnumerable<XElement> source)
{
return new XmlNodeList (source);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: