Kzu's Today I Learned
GitHubX/TwitterBlog
  • Today I Learned
  • dotnet
    • How to emit descriptions for exported JSON schema using JsonSchemaExporter
    • NuGet
      • Suppress dependencies when packing
      • Hide contentFiles from your nuget packages
      • Packaging transitive analyzers with NuGet
      • How to add search to static nuget feed
      • Populate RepositoryBranch in CI for NuGet Pack
    • Ignore folder from dotnet-format
    • Accessing Tor .onion URLs via HttpClient with .NET6
    • Installing .NET 5.0 on Raspberry Pi 4
    • Quickly check C# compiler and language version
    • Disable diagnostic analyzers for entire folder/submodules
    • Persisting output files from source generators
    • Use C# 9 records in non-net5.0 projects
    • AsyncLocal never leaks and is safe for CallContext-like state
    • Using HashCode in .NETFramework
    • How to locate dotnet
  • testing
    • Conditional unit tests
    • Skip tagged scenarios in SpecFlow with Xunit
  • msbuild
    • How to get user home dir ~ cross-platform
    • Modifying the build for every solution in a repository
    • Detect CI builds for every CI system
    • Modify all command-line builds in entire repo
    • Write entire XML fragments in MSBuild with XmlPoke
    • How to select first item in an ItemGroup
    • How to include commit URL in nuget package description
    • How to include package reference files in your nuget
    • How to build project when content files change
  • azure
    • How to launch multiple Azure Functions apps on different ports
    • C# script function apps beyond Azure portal
    • Publishing function app from GitHub folder
    • Exploring Azure Data with Kusto and Dashboards
    • Shared secret authorization with Azure SignalR Service
    • Using Azure File Copy from DevOps yaml pipeline
    • Code-less redirection with serverless Azure Functions
  • DevOps/CI/CD
    • How to run Azure Storage unit tests in CI
    • How to skip steps or jobs in GitHub Actions for PRs from forks
    • Update version and publish npm from GH
    • Push to protected branch from GitHub actions
Powered by GitBook
On this page
  1. msbuild

Write entire XML fragments in MSBuild with XmlPoke

PreviousModify all command-line builds in entire repoNextHow to select first item in an ItemGroup

Last updated 3 years ago

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 to refresh the parameters and format. To my surprise, I found this example:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Namespace>
        <Namespace Prefix="dn" Uri="http://schemas.microsoft.com/appx/manifest/foundation/windows10" />
        <Namespace Prefix="mp" Uri="http://schemas.microsoft.com/appx/2014/phone/manifest" />
        <Namespace Prefix="uap" Uri="http://schemas.microsoft.com/appx/manifest/uap/windows10" />
    </Namespace>
</PropertyGroup>

<Target Name="Poke">
  <XmlPoke
    XmlInputPath="Sample.xml"
    Value="MyId"
    Query="/dn:Package/mp:PhoneIdentity/@PhoneProductId"
    Namespaces="$(Namespace)"/>
</Target>
</Project>

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 :

  <PropertyGroup>
    <UserPropertyGroup>
      <PropertyGroup>
        <ActiveDebugProfile>$(StartupFile)</ActiveDebugProfile>
      </PropertyGroup>
    </UserPropertyGroup>    
  </PropertyGroup>

  <XmlPoke XmlInputPath="$(MSBuildProjectFullPath).user"
           Value="$(UserPropertyGroup)"
           Query="/msb:Project"
           Namespaces="$(UserProjectNamespace)"/>

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:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <ActiveDebugProfile>Program.cs</ActiveDebugProfile>
  </PropertyGroup>
</Project>

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 to initialize the .user project options properly with the right startup file for multi-startup top-level statements scripts in a single project :).

latest official docs on XmlPoke
resounding YEAHHH
SmallSharp