2010/01/21

Working with XML in PowerShell (2)

In a previous post I wrote about examining XML with PowerShell. However, it is often not only necessary to peek into XML, but also to change it.

Updating XML with NAnt

In NAnt, it is possible to use xmlpoke to update XML in arbitrary files. The following NAnt snippet is taking from the build script of ActiveRecord’s test project and allows to adjust the database settings of the build machine.

  <target name="configure-tests">
    <property name="app.config" value="${build.dir}/${project::get-name()}.dll.config" />
    <xmlpoke
      file="${app.config}"
      xpath="/configuration/activerecord/config/add[@key='connection.connection_string']/@value"
      value="${ar.connection.connection_string.1}" 
    />
  </target>

In the snippet above I have omitted some 10 more xmlpoke tasks that all change values in the same application configuration file.

How can this be done in PowerShell? The preceding post in this series showed how to assign the output of a command to a variable, so we only have to use a command that reads the contents of a file and assign the output to the variable:

    $app_config = $project_path + "\build\net-3.5\debug\Castle.ActiveRecord.Tests.dll.config"
    [xml] $cfg = Get-Content $app_config

Now we have the XML to update available as an instance of XmlDocument and the full power of .NET at our hands to manipulate it. When we just had to traverse a path to change our XML, we could simply use dotted path. But if we have to differentiate siblings by its attribute values, the dotted path becomes unwieldy as we had to use a for-each-loop. But we can use XPath instead:

    $cfg.SelectSingleNode("/configuration/activerecord/config/add[@key='connection.connection_string']").value = $myDatabaseConnString
    $cfg.SelectSingleNode("/configuration/activerecord/config/add[@key='dialect']").value = $myDatabaseDialect
    # ...

Finally, we have to save the updated XML. There is one caveat here: XmlDocuments don’t keep whitespace but normalize where it is deemed to be safe. But if we change generated files instead of manually edited ones, this is only a minor nuisance.

    $cfg.Save($app_config)   

The full script is shown below. As before it is quite short compared to the NAnt XML script.

properties {
    [string] $project_path = "C:\dev\castle\SVN\ActiveRecord"
    $myDatabaseConnString = "PSDBCFG"
    $myDatabaseDialect = "PSDBDIALECT"
}

task default -depends configure_tests

task configure_tests {
    $app_config = $project_path + "\build\net-3.5\debug\Castle.ActiveRecord.Tests.dll.config"
    [xml] $cfg = Get-Content $app_config
    $cfg.SelectSingleNode("/configuration/activerecord/config/add[@key='connection.connection_string']").value = $myDatabaseConnString
    $cfg.SelectSingleNode("/configuration/activerecord/config/add[@key='dialect']").value = $myDatabaseDialect
    # ...
    $cfg.Save($app_config)   
}

2010/01/20

Working with XML in PowerShell (1)

During the last days, I started reading about PowerShell and PSake. While looking at the NAnt scripts that I currently use, I recognized that among others the capability of examining and editing XML files is required.

Examining XML in NAnt

In NAnt, inspecting XML is done with the xmlpeek-task. It takes a file name, an XPath and a property name to fill with the result.

One of the uses of xmlpeek is to find out the current revision of a code base, for example to set the private part of the version number with it. In the Castle Project’s build script this is done this way:

  <target name="common.find-svninfo">
    <!-- For adding SVN revision to builds -->
    <property name="svn.revision" value="0" overwrite="false" />
    <!-- try to update the revision -->
    <exec
      program="svn"
      commandline='info "${project::get-base-directory()}" --xml'
      output="_revision.xml"
      failonerror="false"/>
    <xmlpeek
      file="_revision.xml"
      xpath="/info/entry/@revision"
      property="svn.revision"
      failonerror="false"/>
    <delete file="_revision.xml" failonerror="false" />
    <echo message="INFO: Using Subversion revision number: ${svn.revision}"/>
  </target>

So while this is quite a clever solution to find out the projects current revision, it has some drawbacks, such as being nearly 20 lines of code and that it uses a temporary file. But it does the job, reading the revision from the project folder.

Examining XML in PowerShell

PowerShell treats XML as a first class type and also has some shortcuts available. If we take a look at the result of svn info, we see that it is thankfully simple:

<?xml version="1.0"?>
<info>
  <entry
     kind="dir"
     path="."
     revision="6536">
...
  </entry>
</info>

And because we can traverse XML nodes and attributes in PowerShell like object properties, it is possible to get the revision number even without an XPath expression.

$revision = $rev_xml.info.entry.revision

So now it is only necessary to fill the $rev_xml variable with the output of the svn info command. The NAnt script does this using a temporary file but PowerShell allows to assign the standard output of a command line program directly to a variable:

$rev_xml = svn.exe info --xml 

But PowerShell treats the output of such a program as an array of objects, in this case strings. We have to hint PowerShell that what we want is an XMLDocument instead of an array of strings: 

[xml] $rev_xml = svn.exe info --xml 

That’s all. We now only have to put this together into a PSake build script for testing and reference.

properties {
    [string] $project_path = "C:\dev\castle\SVN\ActiveRecord"
    $revision = 0
}

task default -depends get_revision
task get_revision {
    [xml] $rev_xml = svn.exe info $project_path --xml 
    $revision = $rev_xml.info.entry.revision
    "INFO: Using Subversion revision number: $revision"
}

This whole script is shorter than the snippet of NAnt script I’ve shown at the beginning. Taking only the relevant work, 5 lines of PS script replace 15 lines of NAnt XML.

The script of course ignores the fact that getting the revision number should not be a task but rather a function that is called by a build task’s precondition for example. I will get back this in a while in another post.

2010/01/14

ActiveRecord 2.1 released

Yesterday was ActiveRecord 2.1 released. The binaries can be downloaded on SourceForge.

Most of the new functionalities in this release were contributed by Krzysztof Kozmic, a fellow castle committer, who was a great help to get this release out. More thanks go out to the HornGet team, whose efforts took a lot of work out of the release process. Finally, I want to thank all contributors for patches and spotting bugs.

So now what is new in that release?

Updated Dependencies

Castle.Core and Castle.DynamicProxy were updated to their newest releases (1.2 and 2.2 resp.).

NHibernate has been updated to the last stable version 2.1.4 and compiled against the new Castle libraries.

NHibernate.Linq and NHibernate.Search were compiled against NHibernate 2.1.4.

Default configuration feature

This new feature allows to skip all of the <add/>-tags from your configuration if you are only using the database’s default values. In this case your config will be reduced to:

<activerecord>
    <config db="MsSqlServer2005" csn="MyConnectionStringName"/>
</activerecord>

The default configurations are available for most databases.

Inferring PrimaryKeyType from property type

Inspired by FluentNH, ActiveRecord now infers the PrimaryKeyType from the key’s property type:

  • For Guid PrimaryKeyType.GuidComb is used
  • For string PrimaryKeyType.Assigned is used
  • All others default to PrimaryKeyType.Native as before.

Support for backfield and readonly properties

These property accessors are now available in ActiveRecord. Especially readonly is interesting, it allows to store values computed by the entities in the database and is therefore the complement to a computed table column.