T4 Templates To Generate Classes From XML

In this article you will learn about T4 Templates to generate classes from XML.

I came across an issue in one of the UI Automation project where the Automation developer has to use some elements which were references to UI elements from an XML. Let’s see how the XML was looking like:

UI elements

Now this XML has tag <element> which has to be used in helper methods to find elements. It was something like the following:

XML
The problem was that these type of lines were scattered across the project. I found this issue during the code review step and decided to find an approach how to get rid of such problem and then an idea came in my mind.

T4 Templates to rescue

I had this notion of T4 templates before. I saw and did some fixes in EF generated TT templates but I have never created a template from scratch. To create a template from scratch its always handy to use tools like T4 editor and few more custom utilities which makes life easy.

I knew about Tangible T4 template extension of Visual studio 2013. This extension has a free version which gives you features more than what you wanted, but of course there are many benefits to have pro-licensed as well, but this was enough for me so what I got with Tangible T4 template.

The Editor for syntax highlighting:

CS Code
Figure 1

This was the only feature I was looking for. But there was more, a few VS File templates were also installed and couple of them were having sample code as well to get started which was great.

blank template

I used the Advanced T4 template to add new Template file which has generated a sample template like shown in Figure 1.

Now, I had to change it to give a custom implementation so that I can get desired classes. Because the Editor gave a lot of help and everything was visible so it was easy to differentiate the local code and template code. So all I had to do was read my XML file, iterate through it and generate desired result as classes and constant fields.

The following is the complete code that I used:

  1. <#@ template debug="true" hostSpecific="true" #>  
  2. <#@ output extension=".cs" #>  
  3. <#@ Assembly Name="System.Core.dll" #>  
  4. <#@ Assembly Name="System.Xml.dll" #>  
  5. <#@ Assembly Name="System.Xml.Linq.dll" #>  
  6. <#@ Assembly Name="System.Windows.Forms.dll" #>  
  7. <#@ import namespace="System" #>  
  8. <#@ import namespace="System.IO" #>  
  9. <#@ import namespace="System.Diagnostics" #>  
  10. <#@ import namespace="System.Linq" #>  
  11. <#@ import namespace="System.Xml.Linq" #>  
  12. <#@ import namespace="System.Collections" #>  
  13. <#@ import namespace="System.Collections.Generic" #>  
  14. <#     
  15.    //template code - you get IntelliSense here  
  16.     varstr_fileName = "GlobalActionsElements.xml";  
  17.     varprojDir = System.IO.Path.GetDirectoryName(this.Host.TemplateFile);  
  18.   
  19.   
  20.     string str_XMLFileNamePath = Path.Combine(projDir, str_fileName);  
  21.   
  22.   
  23.     // Certainly you would normally load the model data from a file using   
  24.     // the relative path of the template as shown below:  
  25.     // string SampleInputFileContent = System.IO.File.ReadAllText(   
  26.     // System.IO.Path.GetDirectoryName(this.Host.TemplateFile) + "\\datafile.xml");       
  27. #>  
  28. // This is the output code from your template  
  29. // you only get syntax-highlighting here - not intellisense  
  30. namespace WebTest{  
  31.   
  32.   
  33.     public static class <#= Path.GetFileNameWithoutExtension(str_XMLFileNamePath).Replace(".","") #>  
  34.      {  
  35.     <# GenerateConstants(str_XMLFileNamePath); #>  
  36.      }  
  37. }  
  38.   
  39. <#+    
  40.   // Insert any template procedures here  
  41.   void GenerateConstants(string path)   
  42.   {  
  43.     System.Xml.Linq.XDocument doc = System.Xml.Linq.XDocument.Load(path);  
  44.       
  45.       
  46.     var classes = from c in doc.Descendants("uielements")  
  47.                         where c.NodeType != System.Xml.XmlNodeType.Comment  
  48.                         select c.Descendants();  
  49.   
  50.   
  51.     foreach (var item in classes.ToList())  
  52.     {  
  53.          IEnumerable<string> constants = item.Descendants("element").Select(x =>x.Attribute("Name").Value);  
  54.          if(constants.Count() == 0)  
  55.          {  
  56.               constants = item.Select(x =>x.Attribute("Name").Value);  
  57.          }  
  58.          foreach (var node in constants)  
  59.          {  
  60.             #>  
  61.             public const string <#= node.ToUpper() #> = "<#= node #>";  
  62.                <#+  
  63.          }  
  64.     }  
  65. }  
  66. #>  
complete code

I have attached the screenshot and highlighted the code which would generate. It’s really as simple as it seems. You can debug the code easily by placing a Debug breakpoint any where in the code and right click on the Template and select “Debug Template”.

Debug Template

With Tangible T4 template it allows to compile and run the T4 when you press Ctrl + S key (Save document). That’s it.

Here are the results that I generated using it:

program

What we have achieved using it? Well see for yourself:

Code

For now everybody was happy. But this is not all, Next I’m going to use T4 to generate Enums from values in Domain tables from database. Hope this would help someone looking for something like this.