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 17.0 – Newtonsoft.Json 13.0.1
- 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:
<dependentAssembly> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/> <bindingRedirect oldVersion="188.8.131.52-184.108.40.206" newVersion="220.127.116.11"/> </dependentAssembly>
It makes sure that when an assembly references a version of Newtonsoft.Json that is older than 18.104.22.168, it automatically redirects to use the 22.214.171.124 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.
Many thanks! Great timing, just one day later after publishing I needed this info :-). It was driving me crazy why all other referenced assemblies got packed and deployed by vsix for a project I was writing, just not Newtonsoft.Json 12.0. I was already going on the path of adding it explicitly as vsixmanifest asset… Downgrading the nuget package to 9.0.1 saved me some time!
Thanks Mads. Useful and clear.
This VS strong dependency is pretty understandable. However, is Microsoft working on easing the use of newer Newtonsoft.Json.dll versions (> 9.0.1) for the extensions (as if we were using any other reference/NuGet package)? Having to add the dll directly to the VSIX package does not sound ideal for many reasons.
The version of Newtonsoft.Json will be updated in VS when a internal component needs it. Then we will update Newtonsoft.Json to the latest version. Actually, we’re looking in to updating to version 12 at the moment, but it could take many months before it will be available in an update.
If I remember correctly, Microsoft is giong to be including a new JSON parser in .NET Core 3? One that is more efficient and does less memory alllocations. I’m wondering if VS will be moving to that? I assume not, considering it probably uses features that won’t be available to the .NET Framework. Will VS be ported to .NET Core? I’m wondering if the new JSON parser will support schema validation. Newtonsoft charges for this. IMHO something this fundamental should come with the .NET platform free of charge, like it did with XML.
There are no plans to move to another JSON parser or port VS to .NET Core in the 16.x timeframe.
Hi Mads and thank you for your blog post about VS extensions and Newtonsoft.Json.I’m looking for some help about this topic. I’m currently working on a VS2015 ext which includes SignalR.Core.Client. SignalR use a newer version of NW.Json, so I need to ship my ext with that newer version. Unfortunatelly I’m having no success on this. When I try to connect the SignalR client to a server I get an exception like this:Could not load file or assembly ‘Newtonsoft.Json, Version=126.96.36.199, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed’ or one of its dependencies. The system cannot find the file specified.File name: ‘Newtonsoft.Json, Version=188.8.131.52, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed’Any idea on this?
The problem with this is that it isn’t always practical to reference a lower version – as many third party libraries will force you on to newer versions (even though they don’t make use of newer functionality). There is also another dimension to this, which you haven’t touched on – which is what framework version we are allowed to reference for different versions. Newtonsoft.Json has literally forever shipped breaking the rules on assembly strong naming – i.e. that two assemblies with the same strong name should be the same. Looking at the currently shipping 12.0.1 – the assemblies in the net35 and net45 folders share the same strong name, even though the public API isn’t 100% the same. So if someone installs the net35 version in the GAC, then you can expect a MissingMethodException or similar if you rely on the net45 version… So some guidance around what we can expect in terms of .NET Framework version support would be good.
I can’t comment on the specific Newtonsoft.Json case. But it’s pretty common practice (including from Microsoft) to use the rule that given two published assemblies with the same strong name, one’s API is the strict superset of the other. Specifically, the “newer” one with the later package/file version is a superset of the “older” one. Therefore if you compile against the earlier one, you can guarantee that you’ll see no MissingMethodExceptions.
*: Google “c# binary compatibility” for what that means in more nuanced cases than just adding a new public method.
I understand it’s a common practice – but that doesn’t mean it is the right thing to do. A program compiled against the DLL with the larger surface area can easily pick up the DLL with the smaller surface area if the latter is installed in the GAC. And you can’t necessarily compile against the earlier one all the time. Case in point – ASP.NET MVC 5 – uses Newtonsoft.Json under the hood to do model deserialization. You can break that by installing a lower framework targeted assembly in the GAC. If you control the environment, that’s fine. On a customer machine you can’t dictate what will be installed and what might install libraries in the GAC or not. And neither can you recompile ASP.NET MVC 5 to target the earlier / lower framework version. So while it may be common practice, it isn’t right. It’s just a newer form of DLL hell. https://docs.microsoft.com/en-us/dotnet/framework/app-domains/strong-named-assemblies – ‘Assemblies that have the same strong name should be identical.’ – breaking that rule is a rocky road.
I thought newtonsoft was going to be baked into the .net framework. Is this not the case?
For some reason after updating VS 2019 to 16.1.1 Docker started to crash complaining it can not find Newtonsoft.Json ver 9.0.
I tried these options, but, still doesn’t work. I am trying to use Newtonsoft.json in an SSIS solution via Script Component Task. Follow all these instructions, C# script builds correctly, but each time I run the SSIS package I always get the same errors because Newtonsoft.jston files are not found (Script Component : Runtime Error … Could not load file or assembly ‘Newtonsoft.Json, version =184.108.40.206,…). I tried the latest version 12.2, but the results are the same. I have removed the Script Component reopened the SSIS solution, created a new Script component, but nothing works. Tried resintalling using Nuget console, as well as Nuget manager, also tried referencing directly the .dll file. Nothing works.
I use Visual Studio 2017 (SSDT), Version 15.9.15, X64, .NET 4.7.03056.