Write entire XML fragments in MSBuild with XmlPoke
I recently needed to write an entire project file via a targets (weird, I know). I was dreading all the crazy angle brackets escaping as < and > when I decided to check the latest official docs on XmlPoke to refresh the parameters and format. To my surprise, I found this example:
1
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2
<PropertyGroup>
3
<Namespace>
4
<Namespace Prefix="dn" Uri="http://schemas.microsoft.com/appx/manifest/foundation/windows10" />
5
<Namespace Prefix="mp" Uri="http://schemas.microsoft.com/appx/2014/phone/manifest" />
6
<Namespace Prefix="uap" Uri="http://schemas.microsoft.com/appx/manifest/uap/windows10" />
7
</Namespace>
8
</PropertyGroup>
9
10
<Target Name="Poke">
11
<XmlPoke
12
XmlInputPath="Sample.xml"
13
Value="MyId"
14
Query="/dn:Package/mp:PhoneIdentity/@PhoneProductId"
15
Namespaces="$(Namespace)"/>
16
</Target>
17
</Project>
Copied!
Notice how the $(Namespace) property has beautiful XML inside. Which only makes sense, since, there had to be some advantage in MSBuild being XML, right? So I figured, if the namespaces can be a full nested XML element, could the Value be too? And the answer was a resounding YEAHHH:
1
<PropertyGroup>
2
<UserPropertyGroup>
3
<PropertyGroup>
4
<ActiveDebugProfile>$(StartupFile)</ActiveDebugProfile>
5
</PropertyGroup>
6
</UserPropertyGroup>
7
</PropertyGroup>
8
9
<XmlPoke XmlInputPath="$(MSBuildProjectFullPath).user"
10
Value="$(UserPropertyGroup)"
11
Query="/msb:Project"
12
Namespaces="$(UserProjectNamespace)"/>
Copied!
That writes an entire <PropertyGroup> element into a .user project file!
For the attentive reader: you'd think I made a mistake, since, in XML-land, elements inherit the XML namespace of their parent element. Seems like the XML namespace management in XmlPoke is a bit on the loose side of things: the above snippets are in a .targets file without any xmlns (it's an SDK-style <Project> without namespace, to keep it clean. Yet, the property group is added without messing up the namespace:
1
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2
<PropertyGroup>
3
<ActiveDebugProfile>Program.cs</ActiveDebugProfile>
4
</PropertyGroup>
5
</Project>
Copied!
If it had inserted it without a namespace, per the XML spec, it should have added an xmlns="" to clear the parent Project node namespace. But alas, it didn't, and in this particular case, it makes it way more convenient this way :).
I'm using this in SmallSharp to initialize the .user project options properly with the right startup file for multi-startup top-level statements scripts in a single project :).
Last modified 1mo ago
Copy link