Is there a problem with using the TypeNameAssemblyFormat with PCLs? I have no problems using any other settings with Newtonsoft.Json except when I use this serialization setting.
Here is my Json-related code:
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects,
Formatting = Formatting.Indented,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full
};
var json = JsonConvert.SerializeObject(obj, settings);
var jsonBytes = Encoding.UTF8.GetBytes(json);
return jsonBytes;
When I make the call within the same library where it is declared, it is fine. However, when I make the call from a different PCL that calls the above code, I get the missing method exception. This only occurs when I use the TypeNameAssemblyFormat setting (i.e. if I didn't have to use that setting then I wouldn't be writing this post ;).
I am using PCL profile 7.
Exception (I didn't want to blah the entire stack trace, but I can if anyone thinks that would help):
"System.MissingMethodException: Method not found: 'Void Newtonsoft.Json.JsonSerializerSettings.set_TypeNameAssemblyFormat(System.Runtime.Serialization.Formatters.FormatterAssemblyStyle)'
Although there isn't enough information in the question to confirm the root cause with 100% confidence.. Personally, after some experimentation I am positive that the only plausible explanation is as follows -
In short - In the test which fails, the correct (portable) version of
Newtonsoft.Json.dll
is not getting loaded.In long - There are two tests being performed.
Passes - I presume an exe, which calls into PCL1, which calls into portable version of
NewtonSoft.Json.dll
Fails - I presume another exe, which calls into PCL2, which calls into PCL1, which calls into a version (which version?) of
NewtonSoft.Json.dll
The issue is not that PCL2 calls into PCL1 and somehow fails, due to indirect call being made into
NewtonSoft.Json.dll
. Rather, the issue is, as I am trying to highlight above, the second test happens to be setup in a way, that the wrong / non-portable version ofNewtonSoft.Json.dll
is available for PCL1 to consume.In scenario which fails, imagine that the exe or any other non-portable assembly of that application also takes a dependency on (non-portable) version of
NewtonSoft.Json.dll
. In that case, in the output folder of the application / exe, there will be only one version ofNewtonSoft.Json.dll
, and if it is the non-portable one, then it will fail with above mentioned exception..Further explanation - Why?
The type
System.Runtime.Serialization.Formatters.FormatterAssemblyStyle
is typically defined inmscorlib.dll
. However, this type is not available for Portable class libraries to use (don't know about all profiles, but there are some profiles for sure, which do not have this type available). Hence the portable version ofNewtonSoft.Json.dll
, declares it itself, in it's own assembly.Check the decompiled version of portable
NewtonSoft.Json.dll
in your favorite decompiler. Note line number 3 below.. following snippet is fromNewtonSoft.Json.dll
.Now, when you compile code in a PCL, which references
TypeNameAssemblyFormat
property, presumeable of typeSystem.Runtime.Serialization.Formatters.FormatterAssemblyStyle
defined inNewtonsoft.Json.dll
, following IL is generated (decompiled usingIldasm
) -Note how reference to the type is qualified with the assembly name
[Newtonsoft.Json]
(scroll to the right -----> to see it on theFormatterAssemblyStyle
parameter being passed).Now, if this portable version of
Newtonsoft.Json.dll
, gets replaced with non-portable version (because other parts of the project reference non-portable version), then at runtime, CLR will be unable to find method which matches the above signature (as seen in IL above).. and hence fail withSystem.MissingMethodException
.Unfortunately, the exception itself doesn't give enough information about the complete and exact signature of method it is looking for, including the assembly names.. The type name alone deceptively looks like something that would exist in one of the system dlls (
mscorlib.dll
in this case).. and not portable version ofNewtonsoft.json.dll
.Ok, I've ended up completing the answer of my own question due to the clarity of Vikas' framing of the problem. And the resolution is the standard PCL approach with this type of problem: Create interface, configure container, use DI.
In this case, in my PCL, I created an INewtonsoftJsonSettingsProvider interface that has the two settings I use as properties as follows:
Then, in my PCL, I create a concrete implementation of this class as follows:
Note: I could easily skip the interface and just use this helper class alone, but I like to use interfaces when dealing with the container.
Then, in my PCL where my Newtonsoft serializer exists, I consume the settings from the container instead of directly creating those inside of the serialization methods. I'll go ahead and include that code here too (I abstracted serialization to an interface because of this problem, so I can swap out implementations):
Then, in my consuming non-PCL and non-Xamarin (may work in PCL but Xamarin has problem - see below), I configure the container with the proper
System.Runtime.Serialization.Formatters.FormatterAssemblyStyle
as explained in Vikas' answer:This executes without a problem in my .Net test projects. However, when I went to use this in Xamarin.Android project, I got an error stating that the FormatterAssemblyStyle exists in both Newtonsoft and in MonoAndroid mscorlib. Since Xamarin Studio doesn't seem to do extern aliases, I used reflection and dynamic as follows: