Implementing DSL in C # to Generate Domain-Specific XML Data

I have an outdated HTTP / XML service that I need to interact with for various functions in my application.

I need to create a wide range of requests for the service, so in order to avoid a lot of magic lines littering around the code, I decided to create xml XElement snippets to create an elementary DSL.

For instance.

Instead...

 new XElement("root", new XElement("request", new XElement("messageData", ...))); 

I intend to use:

 Root( Request( MessageData(...) ) ); 

With Root, Request, and MessageData (of course, this is for illustrative purposes), which are defined as static methods that all do something like this:

 private static XElement Root(params object[] content) { return new XElement("root", content); } 

This gives me a pseudo-functional composition style that I like for this kind of task.

My final question is indeed one of sanity / best practices, so it is probably too subjective, however I would appreciate the opportunity to get some feedback independently.

  • I am going to move these private methods to an open static class so that they are easily accessible for any class that wants to compose a message for the service.

  • I also intend to have different functions of the service that have their messages created by specific message building classes to improve the service.

Is this a good way to implement this simple DSL, or am I missing some special sauce that will let me do it better?

The thing that makes me doubtful is that as soon as I switch these methods to another class, I increase the length of these method calls (of course, I still keep the original purpose of removing large magical strings of large volume.) Should I more to worry about the size (loc) of the DSL class, than the brevity of the syntax?

Warnings

Please note that in this case, the remote service is poorly implemented and does not comply with any general messaging standards, for example. WSDL, SOAP, XML / RPC, WCF, etc.

In these cases, it would not be wise to create manually created messages.

In rare cases, when you have to deal with a service like the one in question, and it cannot be redesigned for any reason, the answers below provide some possible solutions to this problem.

+6
source share
4 answers

I noticed this article for building custom XML with C # 4.0, which is great .

The source for the library here is https://github.com/mmonteleone/DynamicBuilder/tree/master/src/DynamicBuilder

There is currently a noticeable drawback, lack of support for the xml namespace. Hope this will be fixed.

As a quick example, here's how to do it.

 dynamic x = new Xml(); x.hello("world"); 

What gives:

 <hello>world</hello> 

Here is another quick example from the article.

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

What gives:

 <user> <firstname>John</firstname> <lastname>Doe</lastname> <email> jdoe@example.org </email> <phone type="cell">(985) 555-1234</phone> </user> 

Using the Ruby Builder library, this way of creating custom XML is similar to concise, to the extent that it borders on β€œfun”!

I marked this as an answer because, although it does not directly talk about β€œusing DSL to create arbitrary XML,” it usually eliminates the need due to the extremely short and dynamic nature of the syntax.

Personally, I think this is the best way to create arbitrary XML in C #, if you have a v4.0 compiler and you need to manually start it, there are, of course, much better ways to generate XML automatically with serialization. Reserve this for XML, which should be in a specific form only for legacy systems.

+1
source

Did you notice that all System.Linq.Xml classes are not closed?

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

Remember that this will not work for reading scripts, you will only have a strong typed graph from XML created by your application through the code.

+2
source

Hand-cranking xml is one of the things that should be automated, if possible.

One way to do this is to capture the definitions of XSD messages from your endpoint and use them to generate C # types using the xsd.exe tool.

You can then create the type and serialize it using the XmlSerializer, which gives you an xml message.

+1
source

Writing this in C # seems very difficult. Create your DSL as an XML dictionary, and then compile it into XSLT by writing a compiler (translator) in XSLT. I have done this many times.

0
source

Source: https://habr.com/ru/post/896622/


All Articles