在c#中实现用于生成特定领域XML的DSL
本文关键字:XML DSL 实现 用于 | 更新日期: 2023-09-27 18:07:24
我有一个遗留的HTTP/XML服务,我需要与它交互以实现应用程序中的各种功能。
我必须为服务创建广泛的请求消息,因此为了避免在代码中散布大量神奇的字符串,我决定创建xml XElement
片段来创建基本的DSL。
。
而不是……
new XElement("root",
new XElement("request",
new XElement("messageData", ...)));
我打算使用:
Root( Request( MessageData(...) ) );
将Root、Request和MessageData(当然,这些只是为了便于说明)定义为静态方法,它们的作用类似于:
private static XElement Root(params object[] content)
{
return new XElement("root", content);
}
这给了我一个伪函数组合样式,我喜欢这种类型的任务。
我的最后一个问题是关于理智/最佳实践的,所以这可能太主观了,但是无论如何我都很感激有机会得到一些反馈。
我打算将这些私有方法转移到公共静态类中,以便任何想要为服务编写消息的类都可以轻松访问它们。
我还打算让服务的不同功能通过特定的消息构建类创建它们的消息,以提高可维护性。
这是实现这个简单DSL的好方法吗,还是我缺少了一些能让我做得更好的特殊调味品?
让我怀疑的是,一旦我将这些方法移动到另一个类,我就增加了这些方法调用的长度(当然,我仍然保留了删除大容量魔术字符串的初始目标)。我应该更关心DSL语言类的大小(loc),而不是语法简洁性吗?
警告
请注意,在这个实例中,远程服务实现得很差,并且不符合任何通用的消息传递标准,例如WSDL、SOAP、XML/RPC、WCF等。
在这些情况下,创建手工构建的消息显然是不明智的。在极少数情况下,您必须处理像这里所讨论的那样的服务,并且由于某种原因无法重新设计它,下面的答案提供了一些处理这种情况的可能方法。
您是否注意到所有的System.Linq.Xml
类都是而不是密封的?
public class Root : XElement
{
public Request Request { get { return this.Element("Request") as Request; } }
public Response Response { get { return this.Element("Response") as Response; } }
public bool IsRequest { get { return Request != null; } }
/// <summary>
/// Initializes a new instance of the <see cref="Root"/> class.
/// </summary>
public Root(RootChild child) : base("Root", child) { }
}
public abstract class RootChild : XElement { }
public class Request : RootChild { }
public class Response : RootChild { }
var doc = new Root(new Request());
请记住,这不适用于"读取"场景,您将只能从应用程序通过代码创建的XML中获得强类型图形。
手动操作xml是应该自动化的事情之一,如果可能的话。
实现这一点的方法之一是从端点抓取消息传递XSD定义,并使用XSD .exe工具使用它们生成c#类型。然后您可以创建一个类型并使用XmlSerializer序列化它,它将为您输出xml消息。
我注意到这篇文章用c# 4.0构造任意XML,这非常棒。
库的源代码在这里- https://github.com/mmonteleone/DynamicBuilder/tree/master/src/DynamicBuilder
此时,有一个明显的缺陷,不支持xml名称空间。希望这个问题能得到解决。作为一个简单的例子,下面是如何完成的。
dynamic x = new Xml();
x.hello("world");
产生:
<hello>world</hello>
下面是从文章中摘录的另一个快速示例。
dynamic x = new Xml();
// passing an anonymous delegate creates a nested context
x.user(Xml.Fragment(u => {
u.firstname("John");
u.lastname("Doe");
u.email("jdoe@example.org");
u.phone(new { type="cell" }, "(985) 555-1234");
}));
收益率:
<user>
<firstname>John</firstname>
<lastname>Doe</lastname>
<email>jdoe@example.org</email>
<phone type="cell">(985) 555-1234</phone>
</user>
使用Ruby库Builder
后,这种创建任意XML的方法同样简洁,到了"有趣"的地步!
我把它标记为答案,因为,尽管它没有直接谈到"使用DSL创建任意XML",但由于语法的极端简洁和动态特性,它倾向于消除这种需求。
我个人认为这是在c#中创建任意XML的最好方法,如果你有v4.0编译器并且必须手工处理它,当然有更好的方法来自动生成XML序列化。
用c#写这个看起来工作量很大。将DSL设计为XML词汇表,然后将其编译到XSLT中,在XSLT中编写编译器(翻译器)。我做过很多次了