在 C# 中实现 DSL 以生成特定于域的 XML
我有一个旧版 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 等。
在这些情况下,创建手工构建的消息显然是不明智的。
在极少数情况下,您确实必须处理类似此处所讨论的服务,并且无论出于何种原因都无法对其进行重新设计,下面的答案提供了一些处理这种情况的可能方法。
I have a legacy HTTP/XML service that I need to interact with for various features in my application.
I have to create a wide range of request messages for the service, so to avoid a lot of magic strings littered around the code, I've decided to create xml XElement
fragments to create a rudimentary DSL.
For example.
Instead of...
new XElement("root",
new XElement("request",
new XElement("messageData", ...)));
I'm intended to use:
Root( Request( MessageData(...) ) );
With Root, Request and MessageData (of course, these are for illustrative purposes) defined as static methods which all do something similar to:
private static XElement Root(params object[] content)
{
return new XElement("root", content);
}
This gives me a pseudo functional composition style, which I like for this sort of task.
My ultimate question is really one of sanity / best practices, so it's probably too subjective, however I'd appreciate the opportunity to get some feedback regardless.
I'm intending to move these private methods over to public static class, so that they are easily accessible for any class that wants to compose a message for the service.
I'm also intending to have different features of the service have their messages created by specific message building classes, for improved maintainability.
Is this a good way to implement this simple DSL, or am I missing some special sauce that will let me do this better?
The thing that leads me to doubt, is the fact that as soon as I move these methods to another class I increase the length of these method calls (of course I do still retain the initial goal of removing the large volume magic strings.) Should I be more concerned about the size (loc) of the DSL language class, than I am about syntax brevity?
Caveats
Note that in this instance the remote service poorly implemented, and doesn't conform to any general messaging standards, e.g. WSDL, SOAP, XML/RPC, WCF etc.
In those cases, it would obviously not be wise to create hand built messages.
In the rare cases where you do have to deal with a service like the one in question here, and it cannot be re-engineered for whatever reason, the answers below provide some possible ways of dealing with the situation.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我注意到这篇文章用于使用 C#4.0 构造任意 XML这太棒了。
该库的来源在这里 - https://github.com/mmonteleone/DynamicBuilder /tree/master/src/DynamicBuilder
此时有一个显着的不足,没有xml命名空间支持。希望这个问题能够得到解决。
作为一个简单的例子,它是如何完成的。
其结果是:
这是从文章中摘取的另一个简单示例。
结果是:
使用 Ruby 库
Builder
后,这种创建任意 XML 的方法同样简洁,甚至接近“有趣”!我将其标记为答案,因为尽管它没有直接说明“使用 DSL 创建任意 XML”,但由于语法的极其简洁和动态特性,它往往会消除这种需求。
我个人认为,如果您有 v4.0 编译器并且必须手动启动它,那么这是在 C# 中创建任意 XML 的最佳方法,当然还有更好的方法通过序列化自动生成 XML。将此保留给 XML,它必须采用仅适用于遗留系统的特定形式。
I noticed this article for constructing arbitrary XML with C#4.0 which is great.
The source for the library is here - https://github.com/mmonteleone/DynamicBuilder/tree/master/src/DynamicBuilder
At this time, there is a notable deficiency, no xml namespace support. Hopefully that will get fixed though.
As a quick example, here's how it's done.
Which yields:
Here's another quick example yanked from the article.
Which yields:
Having used the Ruby library
Builder
this method of creating arbitrary XML is similarly terse, to the point that it verges on "fun"!I've marked this as the answer, because, even though it doesn't directly speak to "using a DSL to create arbitrary XML" it tends to remove the need due to the extremely terse and dynamic nature of the syntax.
Personally I think this is the best way to create arbitrary XML in C# if you have the v4.0 compiler and have to crank it by hand, there are of course much better ways to generate XML automatically with serialization. Reserve this for XML which must be in a specific form for legacy systems only.
用 C# 编写此代码似乎需要大量工作。将您的 DSL 设计为 XML 词汇表,然后将其编译为 XSLT,用 XSLT 编写编译器(翻译器)。我已经做过很多次了。
Writing this in C# seems an awful lot of work. Design your DSL as an XML vocabulary, and then compile it into XSLT, writing the compiler (translator) in XSLT. I've done this many times.
您是否注意到所有
System.Linq.Xml
类都未密封?请记住,这不适用于“读取”场景,您只能获得应用程序通过代码创建的 XML 中的强类型图。
Have you noticed that all the
System.Linq.Xml
classes are not sealed?Remember this won't work for 'reading' scenarios, you will only have the strong-typed graph from the XML that your application creates via code.
如果可能的话,手摇 xml 是应该自动化的事情之一。
执行此操作的方法之一是从端点获取消息传递 XSD 定义,并使用 xsd.exe 工具使用它们生成 C# 类型。
然后,您可以创建一个类型并使用 XmlSerializer 对其进行序列化,这将为您输出 xml 消息。
Hand-cranking xml is one of the things which should be automated if possible.
One of the ways of doing this is to grab the messaging XSD definitions off your endpoint and use them to generate C# types using the xsd.exe tool.
Then you can create a type and serialize it using the XmlSerializer, which will pump out your xml message for you.