Home What's New Properties Command Line Managing Properties MSI Integration Samples Roadmap



What's New

2.0.16

New Features

2.0.15

New Features

2.0.14
This release contains a very minor change in behavior to the order that settings files are read.


Issues Fixed

This allows you to do this:
xmlpreprocess.exe /x:spreadsheet.xml /s:overrides.xml ...
Where spreadsheet.xml is an Excel spreadsheet, and overrides.xml is an XML file that contains settings you want to override from the spreadsheet. For example if you have a spreadsheet setting named "foo", and overrides.xml looks like this, then "override value" will be used instead of whatever is in the spreadsheet.
<settings>
<property name="foo">override value</property>
</settings>
Previously this wasn't possible because the XML settings files were read first, and the spreadsheets were read second and overwrote whatever was in the XML settings files.

 

2.0.13
This release is a very minor bug fix release.

Issues Fixed

Example:
xmlpreprocess /i file1.config;file2.config; ...
Before:
${Xpath=configuration/...
After:
${Xpath=/configuration/...

 

2.0.12

New Features


Issues Fixed



For Each
Imagine you have a semicolon delimited list of server names in a property value like Servers=Server1;Server2;Server3, and you would like to repeat a section of XML configuration for each server. There isn’t a good way to do this with previous versions of XmlPreprocess. Version 2.0.12.0 introduces a #foreach construct.
Examples:
Given a property like: Servers=Server1;Server2

<servers>
  <server name="Server1"/>
  <server name="Server2"/>
</servers>

Given a property like: Servers=Server1;Server2;Server3

<servers>
  <server name="Server1"/>
  <server name="Server2"/>
  <server name="Server3"/>
</servers>

Given two properties like: Servers=Server1;Server2, and Addresses=Address1;Address2

<servers>
  <server name="Server1" address="Address1"/>
  <server name="Server2" address="Address2"/>
</servers>

Example 1 - Simple Case
The body of the #ifdef will be repeated once for each value in the semicolon delimited list of the SomeVal property.
Input.xml:

<xml>
  <!-- #ifdef _xml_preprocess -->
  <!-- #foreach(SomeVal) <entry foo="${SomeVal}"/> -->
  <!-- #else -->
  <entry foo="One"/>
  <!-- #endif -->
</xml>

Command:
XmlPreprocess /i:Input.xml /d:SomeVal=One;Two
Output:

<xml>
  <!-- #ifdef _xml_preprocess -->
  <!-- #foreach(SomeVal) <entry foo="${SomeVal}"/> -->
  <!-- #else -->
  <entry foo="One"/>
  <entry foo="Two"/>
  <!-- #endif -->
</xml>

Example 2 - Multiple Values
Two properties can be expanded in parallel into one section of markup.
Input.xml

<xml>
  <!-- #ifdef _xml_preprocess -->
  <!-- #foreach(SomeVal,SomeOtherVal) <entry foo="${SomeVal}" bar="${SomeOtherVal}"/> -->
  <!-- #else -->
  <entry foo="One" bar="Alpha"/>
  <!-- #endif -->
</xml>

Command:
XmlPreprocess /i:Input.xml /d:SomeVal=One;Two /d:SomeOtherVal=Alpha;Beta
Output:

<xml>
  <!-- #ifdef _xml_preprocess -->
  <!-- #foreach(SomeVal,SomeOtherVal) <entry foo="${SomeVal}" bar="${SomeOtherVal}"/> -->
  <!-- #else -->
  <entry foo="One" bar="Alpha"/>
  <entry foo="Two" bar="Beta"/>
  <!-- #endif -->
</xml>

Example 3 - Multiple Values with different lengths
The two lists do not have to be the same length. Note that SomeVal and SomeOtherVal have different number of items.
Input.xml

<xml>
  <!-- #ifdef _xml_preprocess -->
  <!-- #foreach(SomeVal,SomeOtherVal) <entry foo="${SomeVal}" bar="${SomeOtherVal}"/> -->
  <!-- #else -->
  <entry foo="One" bar="Alpha"/>
  <!-- #endif -->
</xml>

Command:
XmlPreprocess /i:Input.xml /d:SomeVal=One;Two /d:SomeOtherVal=Alpha
Output:

<xml>
  <!-- #ifdef _xml_preprocess -->
  <!-- #foreach(SomeVal,SomeOtherVal) <entry foo="${SomeVal}" bar="${SomeOtherVal}"/> -->
  <!-- #else -->
  <entry foo="One" bar="Alpha"/>
  <entry foo="Two" bar=""/>
  <!-- #endif -->
</xml>

Example 4 - Repeated body spans multiple lines
The expanded section can be multiple lines.
Input.xml

<xml>
  <!-- #ifdef _xml_preprocess -->
  <!-- #foreach(SomeVal,SomeOtherVal)
  <wrapper>
    <entry foo="${SomeVal}"
           bar="${SomeOtherVal}"/>
  </wrapper>
  -->
  <!-- #else -->
  <wrapper>
    <entry foo="One"
           bar="Alpha"/>
  </wrapper>
  <!-- #endif -->
</xml>

Command:
XmlPreprocess /i:Input.xml /d:SomeVal=One;Two /d:SomeOtherVal=Alpha
Output:

<xml>
  <!-- #ifdef _xml_preprocess -->
  <!--
  #foreach(SomeVal,SomeOtherVal)
  <wrapper>
    <entry foo="${SomeVal}"
           bar="${SomeOtherVal}"/>
  </wrapper>
  -->
  <!-- #else -->
  <wrapper>
    <entry foo="One"
           bar="Alpha"/>
  </wrapper>
  <wrapper>
    <entry foo="Two"
           bar=""/>
  </wrapper>
  <!-- #endif -->
</xml>

Example 5 - Repeated body spans multiple comments
The expanded section can be in multiple comments.
Input.xml

<xml>
  <!-- #ifdef _xml_preprocess -->
  <!-- #foreach(SomeVal,SomeOtherVal) -->
  <!-- <wrapper> -->
  <!--   <entry foo="${SomeVal}" bar="${SomeOtherVal}"/> -->
  <!-- </wrapper> -->
  <!-- #else -->
  <wrapper>
    <entry foo="One" bar="Alpha"/>
  </wrapper>
  <!-- #endif -->
</xml>

Command:
XmlPreprocess /i:Input.xml /d:SomeVal=One;Two /d:SomeOtherVal=Alpha
Output:

<xml>
  <!-- #ifdef _xml_preprocess -->
  <!--
  #foreach(SomeVal,SomeOtherVal)
  <wrapper>
  <entry foo="${SomeVal}" bar="${SomeOtherVal}"/>
  </wrapper>
  -->
  <!-- #else -->
  <wrapper>
  <entry foo="One" bar="Alpha"/>
  </wrapper>
  <wrapper>
  <entry foo="Two" bar=""/>
  </wrapper>
  <!-- #endif -->
</xml>



Xpath on include
An optional xpath expression can be specified on an #include to address a single node in the included XML document that is to be included. The XPath expression can also contain macros.

--Input.xml--

<xml>
  <!-- #include "Include.xml" xpath="/environments/environment[@name='${_environment_name}']" -->
</xml>

--Include.xml--

<environments>

  <environment name=""Development"">
    <connectionStrings>
      <developmentConnectionStrings/>
    </connectionStrings>
  </environment>

  <environment name=""Production"">
    <!-- COMMENT -->
    <connectionStrings>
      <productionConnectionStrings/>
    </connectionStrings>
  </environment>

</environments>


--Command Line--
Xmlpreprocess /i:Input.xml /e:Production /o:Output.xml

--Output.xml--

<xml>
  
    <!-- COMMENT -->
    <connectionStrings>
      <productionConnectionStrings />
    </connectionStrings>
  
</xml>



2.0.11

What's new in this release:
This is a large release containing a number of enhancements based on feedback given through the discussion forums and issue tracker. Thanks for your excellent suggestions, and keep them coming. I couldn't get to all of them, but wanted to get this release out as soon as possible. Backward compatibility is a priority, so I'm always trying to figure out how to add your new feature requests without impacting any existing behavior. With this release, I'm abandoning the concept of labelling releases as Beta or Release Candidates, and just going to a straight numbered versioning system. It became clear that with all of the great ideas out there, it will be difficult to call it "done", so this is version 2.0.11.


New Preferred Directive Syntax
XmlPreprocess now optionally supports an alternate directive syntax using the hash symbol (#ifdef instead of ifdef). This is more consistent with preprocessors in other languages.

<!-- #ifdef setting -->
<!-- #else -->
<!-- #endif -->

This old format is still supported, but it is now preferred to write your directives using this syntax. Note that leaving the ${ } out from the ifdef condition is also more "proper". (<!-- #ifdef setting --> instead of <!-- #ifdef ${setting} -->). I will try to convert the documentation over to using the new syntax, but it's in a lot of places, and it may take a while to get them all.

New Validation Arguments
New command line arguments to enable two different types of validation individually. It became necessary to control validation separately when the new /noDirectives argument enabled non-XML formats (see: http://xmlpreprocess.codeplex.com/Thread/View.aspx?ThreadId=60626). Previously there was only one switch (/validate or /v) that validated whether all settings existed and that the output was well-formed XML. Now there are three switches:


New built-in property ${_environment_name}
A new built-in property ${_environment_name} was added to contain the name of the environment passed in with the /e argument (see http://xmlpreprocess.codeplex.com/WorkItem/View.aspx?WorkItemId=3807). This property comes in handy when paired with the new Includes feature.

input.xml:

<xml>
    <!-- #ifdef _xml_preprocess -->
    <!-- <entry foo="${_environment_name}"/> -->
    <!-- #else -->
    <entry foo="Development"/>
    <!-- #endif -->
</xml>

Command Line:
xmlpreprocess /i input.xml /x SettingsSpreadsheet.xml /e Production

Output:

<xml>
    <!-- #ifdef _xml_preprocess -->
    <!-- <entry foo="${_environment_name}"/> -->
    <!-- #else -->
    <entry foo="Production"/>
    <!-- #endif -->
</xml>


Ability to define properties inside the input file
You can now define properties inside the input file in define statements (see http://xmlpreprocess.codeplex.com/WorkItem/View.aspx?WorkItemId=3823)

input.xml:

<xml>
   <!-- #define SomeNewValue = "new_value" -->
   <!-- #ifdef _xml_preprocess -->
   <!-- <entry foo="${SomeNewValue}" /> -->
   <!-- #else -->
   <entry foo="dev_value" />
   <!-- #endif -->
</xml>

Command Line:
xmlpreprocess /i input.xml

Output:

<xml>
   <!-- #define SomeNewValue = "new_value" -->
   <!-- #ifdef _xml_preprocess -->
   <!-- <entry foo="${SomeNewValue}" /> -->
   <!-- #else -->
   <entry foo="new_value" />
   <!-- #endif -->
</xml>

Or alternately, a setting could be renamed like this:

input.xml:

<xml>
   <!-- #define SomeNewValue = ${sometoken} -->
   <!-- #ifdef _xml_preprocess -->
   <!-- <entry foo="${SomeNewValue}" /> -->
   <!-- #else -->
   <entry foo="dev_value" />
   <!-- #endif -->
</xml>

Command Line:
xmlpreprocess /i input.xml /d sometoken=new_value

Output:

<xml>
   <!-- #define SomeNewValue = ${sometoken} -->
   <!-- #ifdef _xml_preprocess -->
   <!-- <entry foo="${SomeNewValue}" /> -->
   <!-- #else -->
   <entry foo="new_value" />
   <!-- #endif -->
</xml>


Includes
You can now include external XML files using the <!-- #include "external-file.xml" --> syntax. (see http://xmlpreprocess.codeplex.com/Thread/View.aspx?ThreadId=44434). If the path is relative, it will be resolved from the location of the file being processed.

input.xml:

<xml>
   <!-- #include "include.xml" -->
</xml>

include.xml:

   <!-- #ifdef _xml_preprocess -->
   <!-- <entry foo="${val1}" /> -->
   <!-- #else -->
   <entry foo="bar" />
   <!-- #endif -->

Command Line:
xmlpreprocess /i input.xml /d val1=abc

Output:

<xml>
   <!-- #ifdef _xml_preprocess -->
   <!-- <entry foo="${val1}" /> -->
   <!-- #else -->
   <entry foo="abc" />
   <!-- #endif -->
</xml>

The file name can contain macros that will be resolved during preprocessing. When coupled with the new _environment_name property, some interesting things can be done by having external environment-specific fragment included as shown below. (Alternatively the entire file could be specified in a macro like this <!-- #include "${includedFileName}" -->)

input.xml:

<xml>
   <!-- #include "included-${_environment_name}.xml" -->
</xml>

included-Production.xml:

   <!-- #ifdef _xml_preprocess -->
   <!-- <entry foo="${val1}" /> -->
   <!-- #else -->
   <entry foo="bar" />
   <!-- #endif -->

Command Line:
xmlpreprocess /i input.xml /d val1=abc /e Production

Output:

<xml>
   <!-- #ifdef _xml_preprocess -->
   <!-- <entry foo="${val1}" /> -->
   <!-- #else -->
   <entry foo="abc" />
   <!-- #endif -->
</xml>


${} Escaping
You can now have property values that contain the ${..} sequence in them by escaping them using $${{..}} syntax. (see http://xmlpreprocess.codeplex.com/Thread/View.aspx?ThreadId=61074)

input.xml:

<xml>
   <!-- #ifdef _xml_preprocess -->
   <!-- <entry foo="${PROPERTY}"/> -->
   <!-- #else -->
   <entry foo="abc"/>
   <!-- #endif -->
</xml>

Command Line:
xmlpreprocess /i input.xml /d PROPERTY=$${{newvalue}}

Output:

<xml>
   <!-- #ifdef _xml_preprocess -->
   <!-- <entry foo="${PROPERTY}"/> -->
   <!-- #else -->
   <entry foo="${newvalue}"/>
   <!-- #endif -->
</xml>


Nested Comments
XML comments cannot be nested. This made it impossible to include comments in the commented-out #ifdef branch. You can now embed specially formatted "spaced out" comments in the commented out branch using the format < ! - - and - - > (Note the additional space between each character). These comments will get transformed into regular XML comments upon preprocessing (see http://xmlpreprocess.codeplex.com/Thread/View.aspx?ThreadId=49128).

input.xml:

<xml>
   <!-- #ifdef _xml_preprocess -->
   <!--
   < ! - - This is a nested comment - - >
   <entry foo="${PROPERTY}"/>
   -->
   <!-- #else -->
   <entry foo="abc"/>
   <!-- #endif -->
</xml>

Command Line:
xmlpreprocess /i input.xml /d PROPERTY=newvalue

Output:

<xml>
   <!-- #ifdef _xml_preprocess -->
   <!--
   < ! - - This is a nested comment - - >
   <entry foo="${PROPERTY}"/>
   -->
   <!-- #else -->
   <!-- This is a nested comment -->
   <entry foo="newvalue"/>
   <!-- #endif -->
</xml>


Embedded C# Scripts
Property values can now contain embedded C# scripts that evaluate to a string. Rather than implementing a full expression language like NAnt did, I took a very simple approach of taking the expression and dynamically compiling it into a C# class and executing it. This is not a terribly efficient way to do it, but it does unlock the full power and expressiveness of C# and the .NET framework. To retrieve the value of a property use the built-in GetProperty("propertyname") method. To test if a property is defined use the built-in defined("propertyname") method.

input.xml:

<xml>
   <entry foo="${Script= GetProperty("val1")=="abc" ? "yes" : GetProperty("val3") }" />
   <entry foo="${Script= GetProperty("val1").IndexOf("a")==-1 ? "yes" : GetProperty("val3") }" />
   <entry foo="${Script= GetProperty("val1") + GetProperty("val3") }" />
</xml>

Command Line:
xmlpreprocess /i input.xml /d val1=abc /d val3=no /n

Output:

<xml>
   <entry foo="yes" />
   <entry foo="no" />
   <entry foo="abcno" />
</xml>


#if expressions
A new #if directive has been added that can contain embedded C# scripts that evaluate to a boolean value. Rather than implementing an expression language like NAnt did, I took a very simple approach of taking the expression and dynamically compiling it into a C# class and executing it. This is not a terribly efficient way to do it, but it does unlock the full power and expressiveness of C# and the .NET framework. To retrieve the value of a property use the built-in GetProperty("propertyname") method. To test if a property is defined use the built-in defined("propertyname") method (example: <!-- #if defined("DoServerCheck") -->).

In this example the substitution would only be done when run on a machine named SERVER1 or SERVER2, but not on any other machine.

input.xml:

<xml>
   <!-- #if GetProperty("PrimaryServers").IndexOf(GetProperty("_machine_name")) > -1 -->
   <!-- <entry foo="${SomeValue}"/> -->
   <!-- #else -->
   <entry foo="False"/>
   <!-- #endif -->
</xml>

Command Line:
xmlpreprocess /i input.xml /d SomeValue=abc /d DoServerCheck=True /d PrimaryServers=SERVER1;SERVER2

Output (when run on SERVER1 or SERVER2):

<xml>
   <!-- #if GetProperty("PrimaryServers").IndexOf(GetProperty("_machine_name")) > -1 -->
   <!-- <entry foo="${SomeValue}"/> -->
   <!-- #else -->
   <entry foo="abc"/>
   <!-- #endif -->
</xml>


More details can be found on the release notes for each release referenced below
RC2

RC1

Beta 8

Beta 7

Beta 6

Beta 5

Beta 4

Beta 3

Beta 2

Beta 1