jsonvisual-studio.net-coremsbuild

How can I apply a transformation to a JSON file during build in Visual Studio?


I have a project A (.NET Core) that is referenced by a project B. In project A, I have a JSON file that needs to be updated at compile time (to replace some values) and I would like the JSON file in the build output of project B to be replaced as well.

Sln : 
    Project A
        file.json (build action : none, copy always)

    Project B
        References
            Project A

Content of file A before build is something like :

{
    key : {{this will be replaced during build}}
}

After build it is something like :

{
    key : "VALUE"
}

So far, I tried with post build events in Project A and this works well for the bin folder of project A, but in the bin folder of project B, I have the file.json before string replacement.

I tried disabling the accelerated build as I thought this might be the issue but it does not seem to change the output.

I also tried with a msbuild task :

  <UsingTask TaskName="TestReplace" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
      <ParameterGroup />
      <Task>
          <Reference Include="System.Xml" />
          <Using Namespace="System" />
          <Using Namespace="System.IO" />
          <Code Type="Fragment" Language="cs">
              <![CDATA[
var path = "file.json";
var fileContent = File.ReadAllText(path);
fileContent = fileContent.Replace("{{this will be replaced during build}}", "VALUE");
File.WriteAllText(path, fileContent);
]]>
          </Code>
      </Task>
  </UsingTask>

  <Target Name="TestReplace" AfterTargets="CopyFilesToOutputDirectory">
      <TestReplace />
  </Target>

But with the same result (JSON is OK in output folder for project A, but not for project B)

Is this the intended behavior or did I misconfigure anything? And if this is indeed the intended behavior, what would be the recommended way to achieve the string replacement both in project A and in all the projects that depend on it?

I looked into Single File Generators but it sounds like a complicated solution, and I would prefer to avoid installing a custom extension on all devs machines.

Thanks !

edit : for anyone wondering, I ended up doing something like this :

<None Update="file.json">
    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
    <None Update="tmp\file.json" Link="file.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="do something that write from file.json to tmp/file.json" />
</Target>

and I added tmp/ to the .gitignore


Solution

  • The problem is that Project B doesn't actually copy files from Project A's output folder.

    During the execution project B's GetCopyToOutputDirectoryItems target, the json is being picked up directly from A and added to B's _TransitiveItemsToCopyToOutputDirectory list (which is later dumped in B's output).

    What you can do instead is ensure that the file added to A's <None> list is already transformed, not the original file. So, hook your target earlier into the build process, perform the transformation, then add the transformed file into None.