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。测试代码如下:
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
[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());
}
}
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方法。
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 ;
}
}
{
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 >
< 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