Using Newtonsoft.Json in a Visual Studio extension
The ever popular Newtonsoft.Json NuGet package by James Newton-King is used throughout Visual Studio’s code base. Visual Studio 2015 (14.0) was the first version to ship with it. Later updates to Visual Studio also updated its Newtonsoft.Json version when an internal feature needed it. Today it is an integral part of Visual Studio and you can consider it a part of the SDK alongside other Visual Studio assemblies.
Extensions can therefore also use the very same Newtonsoft.Json shipped with Visual Studio. It can, however, be confusing to know what version to reference and whether to ship the Newtonsoft.Json.dll file itself with the extension or not. And what if the extension supports older version of Visual Studio that doesn’t come with Newtonsoft.Json?
I promise it’s not confusing once you know how, so let’s start at the beginning with versioning.
Just like any other Visual Studio SDK assemblies, extensions must reference lowest version matching the lower bound of supported Visual Studio versions. For instance, if the extension supports Visual Studio 14.0, 15.0, and 16.0, then it must reference the 14.0 SDK assemblies. The same is true for referencing Newtonsoft.Json, but it is less obvious to know what version shipped when.
Here’s the breakdown:
- Visual Studio 16.2 – Newtonsoft.Json 12.0.2
- Visual Studio 16.0 – Newtonsoft.Json 9.0.1
- Visual Studio 15.3 – Newtonsoft.Json 9.0.1
- Visual Studio 15.0 – Newtonsoft.Json 8.0.3
- Visual Studio 14.0 – Newtonsoft.Json 6.0.x
- Visual Studio 12.0 – none
So, if your extension’s lowest supported Visual Studio version is 14.0, then you must reference Newtonsoft.Json version 6.0.x. In fact, make sure the entire dependency tree of your references doesn’t exceed that version.
Learn more about Visual Studio versioning in the blog post Visual Studio extensions and version ranges demystified.
When referencing a lower version of Newtonsoft.Json than ships in Visual Studio, a binding redirect is in place to automatically change the reference to the later version at runtime. Here’s what that looks like in the devenv.exe.config file of Visual Studio 15.0:
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="184.108.40.206-220.127.116.11" newVersion="18.104.22.168"/>
It makes sure that when an assembly references a version of Newtonsoft.Json that is older than 22.214.171.124, it automatically redirects to use the 126.96.36.199 version that ships in Visual Studio 15.0.
This is the same mechanism that makes it possible to use an SDK assembly such as Microsoft.VisualStudio.Language.Intellisense version 12.0 in Visual Studio 16.0. A binding redirect automatically changes the reference to the 16.0 version of that assembly.
Don’t ship it unless you need to
The rule of thumb is to not ship the Newtonsoft.Json.dll file in the .vsix container. Since Visual Studio always have a copy and does binding redirects, there is no reason to ship it.
However, there are two scenarios where you do want to ship the .dll with the extension.
- If your extension supports Visual Studio 12.0 or older
- If you absolutely need a newer version than shipped by Visual Studio
When supporting Visual Studio 12.0 or older, try to use Newtonsoft.Json version 6.0.x if possible. That ensures that when the extension runs in Visual Studio 14.0 and newer, then the .NET Framework won’t load the assembly from your extension, but instead use the one it ships with. That means fewer assemblies needed loading by the CLR.
If you ship your own version, then don’t expect to be able to exchange Newtonsoft.Json types with other assemblies in Visual Studio because they were compiled against a different version. Normally binding redirects unifies the versions, but not when shipping your own. Also specify a code base for it so Visual Studio can resolve it at runtime. You don’t always need to, but it’s considered best practice and avoids any issues. Simply add this line to your AssemblyInfo.cs file:
[assembly: ProvideCodeBase(AssemblyName = "Newtonsoft.Json")]
It’s very important that you never add your own binding redirect for Newtonsoft.Json.dll either. Doing so will force all assemblies in the Visual Studio process to redirect to the version you ship. This might lead to unpredictable issues that could end up breaking other extensions and internal components.
Follow the simple rules
So, the simple rules to apply when using Newtonsoft.Json are:
- Reference the lowest version of Newtonsoft.Json (but no lower than 6.0.x)
- Don’t ship Newtonsoft.Json.dll in the extension
- Except if you target Visual Studio 12.0 or older
- Except if you absolutely need a newer version than ships in Visual Studio
- If you do, specify a code base for it
- Don’t ever add binding redirects for Newtonsoft.Json.dll
I wrote this post based on feedback and questions about how to correctly reference Newtonsoft.Json from an extension. I hope it helped clarify it. If not, please let me know in the comments.