在C#中实现DSL以生成特定于域的XML

时间:2011-09-05 05:28:08

标签: c# xml dsl legacy

我有一个遗留的HTTP / XML服务,我需要与我的应用程序中的各种功能进行交互。

我必须为服务创建各种请求消息,因此为了避免代码中散布着大量魔法字符串,我决定创建xml XElement片段来创建一个基本的DSL。 / p>

例如。

而不是......

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);
}

这给了我一种伪功能组合风格,我喜欢这种任务。

我的终极问题实际上是一种理智/最佳实践,所以它可能过于主观,但我很高兴有机会得到一些反馈。

  1. 我打算将这些私有方法移动到公共静态类,以便任何想要为服务撰写消息的类都可以轻松访问它们。

  2. 我还打算让服务的不同功能通过特定的消息构建类创建消息,以提高可维护性。

  3. 这是实现这个简单DSL的好方法,还是我错过了一些能让我做得更好的特殊酱?

    让我怀疑的是,事实上,只要我将这些方法移动到另一个类,我就会增加这些方法调用的长度(当然我仍然保留了删除大量魔术字符串的初始目标) 。)我是否应该更关注DSL语言类的大小(loc),而不是语法简洁?

    注意事项

    注意,在这种情况下,远程服务实现不当,并且不符合任何通用消息传递标准,例如, WSDL,SOAP,XML / RPC,WCF等。

    在这些情况下,创建手工构建的消息显然不明智。

    在极少数情况下,您必须处理此类服务,并且无论出于何种原因无法重新设计,下面的答案提供了一些处理此情况的可能方法。

4 个答案:

答案 0 :(得分:2)

您是否注意到所有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中的强类型图表。

答案 1 :(得分:1)

手动启动xml是应尽可能自动化的事情之一。

执行此操作的方法之一是从端点获取消息传递XSD定义,并使用它们使用xsd.exe工具生成C#类型。

然后你可以创建一个类型并使用XmlSerializer序列化它,它会为你输出你的xml消息。

答案 2 :(得分:1)

我注意到了article for constructing arbitrary XML with C#4.0 which is great

图书馆的来源是 - 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。为XML保留此选项,XML必须以旧系统的特定形式保留。

答案 3 :(得分:0)

在C#中写这个似乎是一项非常多的工作。将您的DSL设计为XML词汇表,然后将其编译为XSLT,在XSLT中编写编译器(转换器)。我已经多次这样做了。