Use xsd.exe to deal with complex XML messages in .Net

Florian Cailalud
Published by Florian
Category : BizTalk
08/06/2017

CONTEXT

In a BizTalk project, there are several ways to modify or transform XML messages. These may depend on the element in which the process occurs (orchestration, map, or pipeline). It’s possible to perform these transformations with:

  • XPath queries (or XPath function in orchestration case)
  • The set of distinguished fields or promoted properties values (in orchestration case)
  • A sequence of functoids (in map case)
  • Method calls from an external assembly

We choose to deal with XML transformation in orchestrations by using methods (that will be called helpers) from an external assembly. However, the proposed approach can also be used within a pipeline or a map.

This article is organized around the following axes:

  • How to manipulate an XML message as a C# object?
  • In which cases is this approach interesting despite some limitations?

 

SET UP

In order to explain this approach in a concrete example, we propose the implementation of a simple BizTalk application. Thus, we create a BizTalk project named BlogProject_XSDTool within a BlogProject solution.

In this project, we build a ProductList XML schema containing elements (repeatable node) named Product.

We also add an orchestration containing a logical receive port (for ProductList messages), an Assignment shape (which assigns the input message to the output message) and a logical send port (for ProductList messages).

This project has been deployed into a BizTalk application in which input and output of the orchestration ports are respectively associated with a receive location (through a receive port) and a send port, both of type FILE.

This construction allows to drop an XML ProductList file in an input directory and retrieve an output ProductList message as a file in another directory.

Finally, we create a BlogProject_Helpers class library project in the BlogProject solution and add it as a reference to the BizTalk project. We will implement XML messages transformation methods in this class library.

 

GENERATION OF A C# CLASS USING XSD.EXE

The goal is to transform an XML message in C# object to manipulate it using the .Net Framework. To do this, we use the XML Schema Definition tool (xsd.exe) that is provided with Visual Studio and appeared in the 2.0 version of the .Net Framework.

This tool generates XML schemas or C# classes from XDR, XML, and XSD files, or C# class (here is the documentation). The interesting part, for this article, is the generation of a C# class from an XML schema (.xsd).

In order to do this, open the Developer Command Prompt of Visual Studio and type the following command:

xsd.exe {path_to_schema}\ProductList.xsd /c /o :{path_to_class_directory}

Please specify the path to your XSD file instead of {path_to_schema} and the path to the folder in which you want to drop the generated class instead of {path_to_class_directory} (in our case, in the BlogProject_Helpers project folder).

Once the command is executed, the file ProductList.cs appears inside the selected folder. Here’s an overview:

This class is serializable. This will enable us to convert its instances into XML messages.

Then, add this class to the BlogProject_Helpers project as “existing item“.

 

USE THE GENERATED CLASS

To use the newly generated class, we create a ProductListBuilder class. This class will allow us, as a first step, to build a C# ProductList instance from an XLANGMessage (documentation) within our orchestration and, after possible changes, to convert this instance in XLANGMessage again.

 

The CustomBTXMessage class need to be added in the BlogProject_Helpers project, allowing the conversion between a serializable object instance and an XLANGMessage, thanks to its BTXMessage inheritance (documentation).

After compiling, we can realize this call in the orchestration Assignment shape:

Obviously, right now, it does nothing more than the simple assignment of the Set up part. But we can already see the advantages and limitations of this approach.

 

– Limitations:

  • Development: There is an obvious programming overcost compared to a simple map (that can be achieved in 10 seconds!)
  • Maintainability: If the used schema changes, you have to generate the class again to be sure that each element and attributes are up-to-date (with a map, the update can be automatic in many cases).
  • Execution time: instance deserialization can be expensive for large messages.

 

– Advantages:

  • Development: This allows to use all the features of the .Net Framework to effectively perform complex processing (see next part), where a map will show its limits even with the use of an XSL script.
  • Maintainability: The code can be tested in a unit test without deployment to validate the schemas evolutions.
  • Portability: This operation can be performed in an orchestration (as in our example), but also in a pipeline component or a map (with a script functoid)

 

UTILITY OF THE APPROACH THROUGH AN EXAMPLE

To sum up previous parts, this approach is not recommended for simple changes that can be achieved more easily with XPath or maps. However, some complex transformations perfectly show the usefulness of a helper.

We will implement the following example. A product has an ID, a name, a colour and a quantity. We want to combine the quantities by merging the Product items, but only when ID, name and colour are the same. Otherwise, we must keep distinct elements.

Thanks to the conversion in C# objects, we will use tools like LINQ To XML. The problem then quickly resolves by changing the ProductListBuilder class in the following way:

The LINQ query (in green) first transforms each product in an instance of anonymous type {Id, Name, Quantity, Color} for an easier manipulation. Then it groups these instances with the key {Id, Name, Color} and cumulates the quantities of the merged elements.

A list of ProductListProduct is then built with linqProducts and assigned to the Product property of the returning ProductList instance.

This method is called in NewProductList() instead of ConstructProductList(). Thus, It’s not necessary to modify the orchestration (another advantage !).

If you deploy the BizTalk project and its dependencies in the BizTalk application, the incoming file (left) can be processed and gives the desired output (right).

 

CONCLUSION

The approach described in this article is ideally applied only to certain situations due to additional costs (development and execution time). If possible, avoid it. But if a complex process (as in our example) is necessary, it’s a clean and relatively straightforward solution that solves the problem both in orchestrations, maps or pipelines.