Created
February 26, 2012 18:02
-
-
Save kamranayub/1918022 to your computer and use it in GitHub Desktop.
MSBuild Inline Task to Transform a Hierarchy of XML Files using XDT Transforms
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0"?> | |
<!-- Base XML file; shared stuff --> | |
<foo xmlns="somens"> | |
<baz /> | |
</foo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0"?> | |
<!-- inherits is relative to the XML file's directory (e.g. ../base.xml would look one directory up) --> | |
<foo inherits="base.xml" xmlns="somens" xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> | |
<bar xdt:Transform="Insert" /> | |
</foo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- This task takes in a XDT transform file and transforms it, following any inheritance chain. | |
There should be at least one base transform for this to work; otherwise just use Microsoft's | |
regular TransformXml task. --> | |
<!-- EXAMPLE USAGE: | |
<TransformXmlHierarchy | |
Source="source.xml" | |
Destination="transformed.xml" | |
TaskDirectory="path/to/directory/of/Microsoft.Web.Publishing.Tasks" /> | |
--> | |
<UsingTask | |
TaskName="TransformXmlHierarchy" | |
TaskFactory="CodeTaskFactory" | |
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" > | |
<ParameterGroup> | |
<Source Required="true" /> | |
<Destination Required="true" /> | |
<TaskDirectory Required="true" /> | |
</ParameterGroup> | |
<Task> | |
<Reference Include="System.Xml" /> | |
<Reference Include="Microsoft.CSharp" /> | |
<Using Namespace="System"/> | |
<Using Namespace="System.Linq"/> | |
<Using Namespace="System.IO" /> | |
<Using Namespace="System.Xml"/> | |
<Using Namespace="System.Reflection" /> | |
<Code Type="Fragment" Language="cs"> | |
<![CDATA[ | |
// TODO: Figure out a way to make the inline task reference this. For the life of me, | |
// it wasn't working, so had to load via Reflection. | |
var taskPath = Path.Combine(TaskDirectory, "Microsoft.Web.Publishing.Tasks.dll"); | |
if (!File.Exists(taskPath)) | |
throw new Exception("Could not load publishing tasks assembly"); | |
Assembly taskAssembly = Assembly.UnsafeLoadFrom(taskPath); | |
Func<XmlDocument, string, XmlDocument> transformer = (source, transform) => | |
{ | |
dynamic transformation = taskAssembly.CreateInstance( | |
"Microsoft.Web.Publishing.Tasks.XmlTransformation", true, BindingFlags.CreateInstance, | |
null, new object[] { transform }, null, null); | |
if (transformation == null) | |
throw new Exception("Could not create instance of XmlTransformation"); | |
transformation.Apply(source); | |
return source; | |
}; | |
Func<XmlDocument, string> getParent = (source) => | |
{ | |
if (source == null) return null; | |
// Use default namespace of document | |
var nsmgr = new XmlNamespaceManager(source.NameTable); | |
nsmgr.AddNamespace("x", source.DocumentElement.NamespaceURI); | |
// TODO: Probably can safely select first node, to support any kind of XML document | |
var attr = source.SelectSingleNode("x:package", nsmgr).Attributes["inherits"]; | |
return attr == null ? null : attr.Value; | |
}; | |
var rootDoc = new XmlDocument(); | |
var sources = new List<string>(); | |
var basePath = Path.GetDirectoryName(Source); | |
var parent = Path.GetFileName(Source); | |
if (basePath == null) { | |
throw new Exception("Could not find base directory of path " + Source); | |
} | |
do { | |
sources.Add(parent); | |
rootDoc.Load(Path.Combine(basePath, parent)); | |
parent = getParent(rootDoc); | |
// TODO: Need to rebase basePath here? | |
if (parent != null) { | |
rootDoc.Load(Path.Combine(basePath, parent)); | |
} | |
} while (parent != null); | |
// Reverse chain | |
sources.Reverse(); | |
var transformedDoc = sources.Skip(1).Aggregate(rootDoc, | |
(document, transform) => String.IsNullOrEmpty(transform) | |
? document | |
: transformer(document, Path.Combine(basePath, transform)), | |
(document) => document); | |
Log.LogMessage(MessageImportance.Normal, "Transformed " + Destination); | |
transformedDoc.Save(Destination); | |
]]> | |
</Code> | |
</Task> | |
</UsingTask> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Import Project="Transforms.xml" /> | |
<Target name="Foo"> | |
<TransformXmlHierarchy | |
Source="source.xml" | |
Destination="transformed\transformed.xml" | |
TaskDirectory="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\" /> | |
</Target> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment