用C#实现仿Ruby的XML Builder

Ruby中有个很好用的XML API,在页面实现XML输出很方便。比如有个联系人Contacts的XML输出,Ruby里面是这样写的。

...
xml.contacts do
  xml.contact(:type => "work") do
    xml.name("Joe Smith")
  end
end
...

借用.Net 4.0的dynamic特性,用C#实现一个XmlBuilder。测试代码如下:

 

XmlBuilderTest
[TestFixture]
public class XmlBuilderTest
{
[Test]
public void CreatorTest()
{
dynamic xmlBuilder
= new XmlBuilder();
var result
= xmlBuilder.Contacts(
xmlBuilder.Contact(
"type", "team-member", "id", "1",
xmlBuilder.Name(
"Joe Smith"),
xmlBuilder.Phone(
"type", "work", "(123) 456-7890"),
xmlBuilder.爱好(
"看电影"),
xmlBuilder.Address(
"type", "home",
xmlBuilder.Street(
"123 Main St."),
xmlBuilder.City(
"SpringField"))));

Console.WriteLine(
"XML: \n" + result.ToString());
}

}

为实现上述功能,我们创建类XmlBuilder,继承DynamicObject,并override其TryInvokeMember方法。

 

XmlBuilder
public class XmlBuilder : DynamicObject
{
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
var arguments
= ParseArguments(args);
var rootElement
= new XElement(binder.Name, arguments.Text, arguments.Attributes);
foreach (XDocument xDocument in arguments.Documents)
rootElement.Add(xDocument.FirstNode);

result
= new XDocument(rootElement);
return true;
}

private static dynamic ParseArguments(object[] arguments)
{
var documents
= new List<XDocument>();
var strings
= new List<string>();
var attributes
= new List<XAttribute>();
string text = null;

//分析参数类型,区分string和XDocument
foreach (var arg in arguments)
{
XDocument xDocArgument;
string stringArgument;
if (CastAs(arg, out xDocArgument))
documents.Add(xDocArgument);
else if (CastAs(arg, out stringArgument))
strings.Add(stringArgument);
}

//如果strings总数为奇数,定义最末一个字符串为InnerText。
if (strings.Count % 2 == 1)
{
text
= strings.Last();
strings.RemoveAt(strings.Count
- 1);
}

//将strings成对封装为XAttribute。
for (var i = 0; i < strings.Count; i=i+2)
{
attributes.Add(
new XAttribute(strings[i], strings[i+1]));
}

//返回动态类型。
return new { Documents = documents, Attributes = attributes, Text = text };
}

/// <summary>
/// 类型判断
/// </summary>
private static bool CastAs<T>(object value, out T castedValue)
{
if (value is T)
{
castedValue
= (T)value;
return true;
}
castedValue
= default(T);
return false;
}
}

运行XmlBuilderTest,得到的结果如下:

 

 

XML:
<Contacts>
<Contact type="team-member" id="1">
<Name>Joe Smith</Name>
<Phone type="work">(123) 456-7890</Phone>
<爱好>看电影</爱好>
<Address type="home">
<Street>123 Main St.</Street>
<City>SpringField</City>
</Address>
</Contact>
</Contacts>

 

 

-- 声明 --

1、没太动脑筋,主要的实现思路(包括部分代码),来源于In a Perfect World。(带梯子可访问)原作者给的代码有问题,我在上面做了修改。

2、运行dynamic,需要在.Net 4.0环境下,引用程序集Microsoft.CSharp.dll。

-- 下载 --

所有代码在这儿下载。XmlBuilderTest.zip

posted @ 2010-09-08 22:11  ZhZhen  阅读(489)  评论(1)    收藏  举报