Newtonsoft.json: System.MissingMethodException when using JsonSerializerSettings.TypeNameAssemblyFormat across PCL and non-PCL projects

Created on 24 May 2016  路  2Comments  路  Source: JamesNK/Newtonsoft.Json

I am working on a large project where we have multiple solutions, including a .NET 4.5.2 console app and a Windows Store App. We have some core libraries that we share, and in order to do so, we have created them as portable class libraries. This gives a nice and clean architecture with lots of code reuse - all good.

We are using JSON.NET as our main tool for JSON work and have been very pleased for many years. However, after we started using the JsonSerializerSettings.TypeNameAssemblyFormat in our shared PCL libraries, we got a System.MissingMethodException in the .NET 4.5.2 console app when consuming these libraries. I have researched into this, and even though I found people with a similar problem ( see: http://stackoverflow.com/q/27080363/700926 ) I did not find any solution.

Issue Description

I have boiled this down to a demo app that I have attached. I will explain the issue with focus on that.

The demo app contains a solution with two projects:

  • JsonDotNetDebug (.NET 4.5.2 console app)
  • JsonDotNetDebug.Portable (PCL targeting Profile 7 / .NET 4.5, ASP.NET Core 1.0, Windows 8)

The PCL contains the following method that is supposed to be re-used among other projects, including the console app in the solution:

using Newtonsoft.Json;

namespace JsonDotNetDebug.Portable
{
    public class Class1
    {
        public JsonSerializerSettings GetJsonSerializerSettings()
        {
            return new JsonSerializerSettings
            {
                /*
                 * Since this mini-API is defined in a PCL, the: "System.Runtime.Serialization.Formatters.FormatterAssemblyStyle"
                 * is not available in mscorlib. Instead, it is located in the Newtonsoft.Json assembly.
                 * See: Newtonsoft.Json/Src/Newtonsoft.Json/FormatterAssemblyStyle.cs
                 * See: https://github.com/JamesNK/Newtonsoft.Json/blob/6d7c94e69fa2f52b91fb22972321cb9b51b9abed/Src/Newtonsoft.Json/FormatterAssemblyStyle.cs
                 */
                TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full
            };
        }
    }
}

In my console app, I then want to use my mini API to get the JsonSerializerSettings:

using System;

namespace JsonDotNetDebug
{
    // Targets: .NET 4.5.2
    public class Program
    {
        public static void Main(string[] args)
        {
            var c1 = new Portable.Class1();

            /* 
             * Throws System.MissingMethodException
             * 
             * If I look in ILDASM, I find the following setter in Portable.Class1:
             * 
             * instance void [Newtonsoft.Json]Newtonsoft.Json.JsonSerializerSettings
             * ::set_TypeNameAssemblyFormat(valuetype [Newtonsoft.Json]System.Runtime.Serialization.Formatters.FormatterAssemblyStyle)
             * 
             * The problem (as I see it) is that the "FormatterAssemblyStyle" is located in the
             * Newtonsoft.Json assembly, but my console app is compiled against full .NET 4.5.2
             * and expects the FormatterAssemblyStyle to be located in mscorlib - hence the exception.
             * 
             */
            var jsonSerializerSettings = c1.GetJsonSerializerSettings();

            /*
             * To solve the ambigiuty, I can force the use of the portable
             * Newtonsoft.Json.dll by adjusting packages.config and the *.csproj file.
             * 
             * However, since this console app is a full .NET 4.5.2 app, the
             * "System.Runtime.Serialization.Formatters.FormatterAssemblyStyle" will then
             * be available from both the mscorlib and Newtonsoft.Json assembly.
             * 
             * This causes a compilation failure, as the compiler don't know
             * which FormatterAssemblyStyle to use (mscorlib vs Newtonsoft.Json).
             * 
             * While this can be solved by introducing an extern alias for the
             * Newtonsoft.Json assembly, it does not seem right.
             */
            jsonSerializerSettings.TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            Console.Read();
            Console.WriteLine("Done");
        }
    }
}

However, as outlined in the source comments, this breaks at runtime. _This is the issue I cannot get my head around_. While the stacktrace does not say much (it lacks the assembly name) I have included it for completion:

System.MissingMethodException was unhandled
  HResult=-2146233069
  Message=Method not found: 'Void Newtonsoft.Json.JsonSerializerSettings.set_TypeNameAssemblyFormat(System.Runtime.Serialization.Formatters.FormatterAssemblyStyle)'.
  Source=JsonDotNetDebug.Portable
  StackTrace:
       at JsonDotNetDebug.Portable.Class1.GetJsonSerializerSettings()
       at JsonDotNetDebug.Program.Main(String[] args) in C:\Users\MyUser\Documents\visual studio 2015\Projects\JsonDotNetDebug\JsonDotNetDebug\Program.cs:line 25
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

As I write in the source comments, I can solve this by force-using the portable version of Newtonsoft.Json.dll in all projects, and then declare an extern alias for Newtonsoft.Json.dll, to tell the compiler to always use the FormatterAssemblyStyle located in Newtonsoft.Json.dll.

While this work, it does not seem right to me? In my eyes, I would have liked the "FormatterAssemblyStyle" to be a custom type in its own Newtonsoft.Json namespace (instead of replicating the System.Runtime.Serialization.Formatters inside Newtonsoft.Json.dll), e.g., the namespace could be: Newtonsoft.Json.FormatterAssemblyStyle? This would put a direct requirement on that type, with no risk for ambiguities. But yeah, I guess there is a reason why the project has chosen to replicate the System.Runtime.Serialization.Formatters namespace inside Newtonsoft.Json.dll?

While that is said, I have a feeling that I am missing something really obvious. If that is the case, please let me know where I go wrong, and I will hopefully learn something new :)

I am looking forward to your response.

Thanks for a great project :+1:

Environment info:

  • Windows 8.1 Enterprise
  • .NET 4.6.1 installed (app is running .NET 4.5.2)
  • VS2015 Enterprise, Update 2
  • Nuget package: Newtonsoft.Json, v. 8.0.3

Attachment Info

For some reason, github won't let me upload a .zip file. So yeah, to get this to work, I have appended a .txt extension.

JsonDotNetDebug.zip.txt

Most helpful comment

Don't know if the developer reads this but this issue is a real concern.

In a cross-platform solution the best way to ensure that serialization/deserialization behaves consistently for all projects is to wrap Netwonsoft.Json with a class that guarantees consistent settings and share it. As swlasse points out, do that in a Shared Class Library and kaboom.

We considered using swlasse's workaround but we too find manually adjusting project files untenable.

Another idea we dismissed is to create our own private NuGet packages containing our shared class, targeting each environment. That's a lot of work. You might well as copy the source around. So...we decided to use a Shared Project which essentially copies the source by reference into each project that references the Shared Project . Yuck...but it works.

The absolute best answer is for the NewtonSoft.Json package to address the issue. I would try but the code in this project sets a pretty high bar. I would probably be embarrassed by the assessment of my pull request.

Lately we find ourselves working on the tools at the expense of our project. Our decision to essentially use linked source files was made simply to minimize our exposure to the bleeding edge regarding .NET Standard and Core which have a long way to go from a feature and tooling perspective.

The Newtonsoft developers should strongly consider giving this issue a high priority.

All 2 comments

Don't know if the developer reads this but this issue is a real concern.

In a cross-platform solution the best way to ensure that serialization/deserialization behaves consistently for all projects is to wrap Netwonsoft.Json with a class that guarantees consistent settings and share it. As swlasse points out, do that in a Shared Class Library and kaboom.

We considered using swlasse's workaround but we too find manually adjusting project files untenable.

Another idea we dismissed is to create our own private NuGet packages containing our shared class, targeting each environment. That's a lot of work. You might well as copy the source around. So...we decided to use a Shared Project which essentially copies the source by reference into each project that references the Shared Project . Yuck...but it works.

The absolute best answer is for the NewtonSoft.Json package to address the issue. I would try but the code in this project sets a pretty high bar. I would probably be embarrassed by the assessment of my pull request.

Lately we find ourselves working on the tools at the expense of our project. Our decision to essentially use linked source files was made simply to minimize our exposure to the bleeding edge regarding .NET Standard and Core which have a long way to go from a feature and tooling perspective.

The Newtonsoft developers should strongly consider giving this issue a high priority.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

danebou picture danebou  路  18Comments

FrancoisBeaune picture FrancoisBeaune  路  121Comments

TylerBrinkley picture TylerBrinkley  路  16Comments

Richard-Payne picture Richard-Payne  路  13Comments

Phreak87 picture Phreak87  路  11Comments