Search code examples
msbuildmsbuild-task

How to create or update the value of an element using XmlPoke


In an MSBuild target, I need to set the value of an XML element to a specific value. If that element does not exist I need to create it. I'm trying to achieve this using MSBuild's XmlPoke task but it only works if the element exists already. Here's an example:

XML content to update:

<?xml version="1.0" encoding="utf-8"?>
<manifest>
  <metadata>
    <name>whatever</name>
    <version>1.2.3.4</version>
  </metadata>
</manifest>

Using XmlPoke as shown below, I can successfully set the value of the version element:

<XmlPoke
  XmlInputPath="$(XmlFilePath)"
  Query="/manifest/metadata/version"
  Value="4.3.2.1" />

The result of that looks as follows:

<?xml version="1.0" encoding="utf-8"?>
<manifest>
  <metadata>
    <name>whatever</name>
    <version>4.3.2.1</version>
  </metadata>
</manifest>

However, if the input is missing the version element, as shown below, the XmlPoke task just doesn't do anything:

<?xml version="1.0" encoding="utf-8"?>
<manifest>
  <metadata>
    <name>whatever</name>
  </metadata>
</manifest>

How can I create the version element and set it to 4.3.2.1 if it does not exist or just set it to 4.3.2.1 if it does exist using MSBuild standard functionality?


Edit:

The accepted answer with combining both XmlPeek and XmlPoke works. Based on that answer, here's the code that inserts or updates the value:

<XmlPeek
   XmlInputPath="$(XmlFilePath)"
   Query="/manifest/metadata/version">
   <Output
      TaskParameter="Result"
      ItemName="VersionEntry" />
</XmlPeek>
<XmlPoke
   Condition="'@(VersionEntry)' != ''"
   XmlInputPath="$(XmlFilePath)"
   Query="/manifest/metadata/version"
   Value="4.3.2.1" />
<XmlPoke
   Condition="'@(VersionEntry)' == ''"
   XmlInputPath="$(XmlFilePath)"
   Query="/manifest/metadata"
   Value="&lt;Name&gt;whatever&lt;/Name&gt;&lt;Version&gt;4.3.2.1&lt;/Version&gt;" />

Solution

  • XmlPoke uses the specified XPath, the Query, to find matching nodes. If it finds a node, it replaces the InnerXml with the specified Value. It will do this for each match. If there are no matches, XmlPoke does nothing.

    An approach using standard MSBuild functionality:

    Use XmlPeek to test for /manifest/metadata/version.

    If version exists, then use XmlPoke to update.

    If version does not exist:

    1. Use XmlPeek to get the contents of /manifest/metadata.
    2. Create a replacement value consisting of the existing contents of metadata with <version>$(ver)</version> appended.
    3. Use XmlPoke to update /manifest/metadata.

    If that seems cumbersome, you could write an inline task that 'upserts' the version.