Posted by: Pete Montgomery on: September 20, 2010
The XML Document Transform language, or XDT, shipped with Visual Studio 2010. It’s an XML dialect designed to help you automatically transform your .NET config files appropriately for your various deployment environments.
Don’t worry, XDT is very simple and specifically designed for the task of transforming configuration files. For this scenario, it is much easier to use than a general-purpose transformation language like XSLT.
Application configuration files tend to be mostly identical across different deployment environments. Only small modifications, such as the value of your connection strings, the debug attribute, and so on, are required to create a production Web.config file out of a development Web.config file.
To show how simple it is, the basic semantics of the language can be expressed in a few lines of C#.
Let’s assume an existing Web.config file for our input document. We’ll also write a small XDT transform document which sets the ASP.NET debug attribute to false:
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> <system.web> <compilation debug="false" xdt:Transform="SetAttributes" /> </system.web> </configuration>
Notice it looks a bit like a small Web.config document. The input doc could be a big, complex Web.config file, but that doesn’t matter – the transform doc only needs to contain the differences.
The Transform attribute is defined within a distinguished XML namespace to ensure that it can’t clash with any existing attributes in the input doc. In this case, we’re using one of the most useful built-in transforms, SetAttributes, which modifies attribute values on the targeted element to those that are specified in the transform doc – in this case, setting debug to false. There are other useful transforms, such as Remove and Replace, which alter the targeted element in different ways.
A transform implicitly specifies the element(s) in the input document to act upon by virtue of the element that it belongs to: expressed in XPath, the above transform would target the
/configuration/system.web/compilation element(s).
Here is a simple implementation of XDT using C#:
public XDocument Transform(XDocument inputDoc, XDocument transformDoc)
{
var workingDoc = new XDocument(inputDoc);
// (1) pair each "Transform" element with the element(s)
// it targets in the working document
var xs = from e in transformDoc.Descendants()
from a in e.Attributes(Namespaces.Xdt + "Transform")
let xpath = GetElementXPath(e)
select new
{
TransformElement = e,
TargetElements = workingDoc.XPathSelectElements(xpath)
};
// (2) apply each transform to its target elements
foreach (var x in xs)
{
ApplyTransform(x.TransformElement, x.TargetElements);
}
return workingDoc;
}
This is a slight simplification, and of course doesn’t show you the implementation of the individual transforms or how to compute the XPath. You can find the full, reasonably complete implementation of XDT on Google Code.
The implicit XPath location of, for example, a /configuration/appSettings/add element would usually specify more than one element in most Web.config files. While this is valid, it’s not very useful. XDT provides a second attribute, Locator, which allows you to augment the implicit XPath expression with a predicate to filter the target element set:
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add xdt:Locator="Condition(@key='key3')" />
</appSettings>
</configuration>
This would give us target XPath of /configuration/appSettings/add[@key='key3'], which is much more useful (although it doesn’t do anything, because we haven’t specified any transform!).
A convenience case of the Condition locator is to simply Match on the specified attribute, so to actually alter a particular app setting value, you would write:
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> <appSettings> <add key="key3" value="new value" xdt:Locator="Match(key)" xdt:Transform="SetAttributes" /> </appSettings> </configuration>
This specifies a SetAttributes transform with the same target XPath as the previous example.
I wanted to use XDT in production before Visual Studio 2010 was released. I also find that thinking about the implementation of something helps you understand its semantics. However, there are some other good reasons:
If you’re creating a serious build system, these restrictions would otherwise probably prevent you from using XDT for your transformations, which would be a shame.
It feels like a classic Microsoft situation – a well-engineered core technology, but restricted (understandably) for commercial / shipping reasons. If you do want to go ahead and use this alternative implementation, it’s very straightforward to create an MSBuild task to call XdtTransformer.Transform() in order to use XDT in your build system. If there’s enough demand, I could add one to the distribution on Google Code.
I don’t have a need for it just yet but its cool that you made an implementation
Great work for those not ready for VS 2010 and for those that need to implement a custom deployment solution.
Great work! The only problem is the source control provider. Would there be a problem for you to switch to Mercurial, so other can also contribute (Google Code has suport for it) ?
I would strongly suggest you consider XSLT instead. There’s a great write-up on how to do it at http://mint.litemedia.se/2010/01/29/transforming-an-app-config-file/. I use this strategy and find it superior to XDT because:
- XSLT is THE standard for transforming XML and there is a lot of documentation on how to do things
- XSLT is not limited to a small subset of operations, and can make ANY desired change to your config file
- The solution presented is not limited to Web Sites, so you can use the same solution for ALL you config files
- Plus the solution he presented has no need for external tasks and suchlike since it integrates directly with the project file.
We want to integrate XDT in Apache NPanday (Maven for .NET)
https://issues.apache.org/jira/browse/NPANDAY-493
We think of using your version because it could possibly also run on Mono (if not we could make it run).
I’m not sure if we can redistribute LGPL-stuff; would you consider also providing XDT under the Apache license?
September 20, 2010 at 4:38 am
I hear the point about “limited” transforms to a document, but it actually seems more clumsy and clunky to me than if I’d just used XSLT (which we do in our project).