Newtonsoft.json: Cross Platform Primitive Types with Generics

Created on 31 Jan 2019  路  18Comments  路  Source: JamesNK/Newtonsoft.Json

Source/destination types

public class TestClass<T>
{
    public int A { get; set; }
    public T B { get; set; }
}

Source/destination JSON

With UWP/.NetCore:

{
    "$type": "AppTest.MainPage+TestClass`1[[System.Int32, System.Private.CoreLib]], AppTest",
    "A": 1,
    "B": 2
}

With .NET Framework

{
    "$type": "AppTest.MainPage+TestClass`1[[System.Int32, mscorlib]], AppTest",
    "A": 1,
    "B": 2
}

Expected behavior

Successful deserialization. Or the suggested fix of serializing primitives without the assembly name:

{
    "$type": "AppTest.MainPage+TestClass`1[[System.Int32]], AppTest",
    "A": 1,
    "B": 2
}

Actual behavior

Runtime Exception:

Newtonsoft.Json.JsonSerializationException: 'Error resolving type specified in JSON 'ConsoleApp3.Program+TestClass`1[[System.Int32, mscorlib]], ConsoleApp3'. Path '$type', line 1, position 81.'

Steps to reproduce

  1. Serialize class with TypeNameHandling (Tested with TypeNameHandling.Objects)
 string serialized = JsonConvert.SerializeObject(a, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Objects});
  1. Deserialize json file on a different .Net system
    a. That is, .NETFramework if UWP/.NETCore, and vice versa.
object deserialized = JsonConvert.DeserializeObject(serialized, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Objects });

All 18 comments

Similar issue: #1378

However, it would be nice if at least the primitive types are supported by default. (Type.IsPrimitive?)

Stumbling on this issue porting my projects from NetFramework to Net5.

I'm surprised this is not a more high profile issue since this is probably happening to many people since the existence of NetCore. Any project using json serialization for messages between micro-services using different framework will encounter this on basic primitive types. #1378 offers a workaround, but it's not perfect, and also requires old NetFramework services to be updated to support the inverse (netcore to netframework).

Since the issue haven't moved since 2019, should I assume it's not in the plan to offer a transparent fix officially?

This repo is (almost) dead. Use System.Text.Json instead. It makes no sense for MS to improve this serializer.

As much as System.Text.Json is a nice initiative and faster and easier to use for basic projects, it is still missing many features so the switch can't be done "as is" for many project. Notably polymorphic deserialization (TypeNameHandling) as mentioned in this issue. Or PopulateObject(), or some other custom converters that needs to be implemented differently.

Yes, there is no parity in features yet. But at least with the appearance of net5 there are more green checkmarks in the second column - https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to?pivots=dotnet-5-0#table-of-differences-between-newtonsoftjson-and-systemtextjson

See also https://github.com/dotnet/runtime/issues/41313

Yeah this issue is definitely going to be seen more and more when porting apps to .NET 5.

I'll share our temporary but not ideal solution was removing all instances of , System.Private.CoreLib and , mscorlib in the serialized text. If you want you could even check for levels of [[ ]] brackets.

I don't know what the status of JSON.net is, but I'd imagine it stay around as System.Text.Json gains more functionality, probably years. But feels weird talking about whenever or not JSON.net will stay around in this issue thread. Might want to at least support this feature as people make the transition, since this would be the biggest obstacle I can think of.

@Dunge This issue occurred when serializing on .NET 5 and then deserializing on .NET Framework 4 right? Want to make sure this fully fixes the issue

The value in $type is written and read by ISerializationBinder. You could write your own one to resolve these name values.

Alternatively you could wrap the default one (DefaultSerializationBinder) and do something hacky like a string replace on the type name. For example, replace mscorlib with System.Private.CoreLib.

potential workaround?

    AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
    {
        if (args.Name == "System.Private.CoreLib")
        {
            return typeof(int).Assembly;
        }

        return null;
    };

@danebou Yes. Somehow, .NET Framework4 serialized strings with mscorlib can deserialize without issue in a .NET5 project, but not the inverse.

@SimonCropp This seems to be working for me. In any case, it's much simpler and cleaner than @arthurvaverko-kaltura workaround in #1378.

In my situation, the System.Private.CoreLib string would not appear on primitive (int, string). It would appear in generics as the issue mention, but also on types like List when TypeNameHandling = TypeNameHandling.All is specified.

@Dunge yeah we confirmed and rolled out the AssemblyResolve workaround

That's a far better solution. I'll close out my PR.

@danebou i didnt consider my suggestion a "fix", just a workaround. My preference would be that this scenario was supported OOTB

Ah gotcha. Yeah, I'm in favor of OOTB too. It at least seems like a cleaner solution. However, it might bring unexpected behavior to the user, if say they relied on that assembly check to determine the platform (very bad implementation, but still).

@JamesNK Regardless of the implementation, is this something you feel like should be handled with JSON.net, or left up to the user to apply a workaround? I think the decision on that would either close out this issue, or allow us to get a PR up.

I'm not sure. I'm hesitant to hardcode behavior on assembly names. If it isn't automatically supported, at the very least the workaround should be added to the documentation.

@JamesNK is previously worked ootb, should it instead be considered a bug in dotnet?

An alternative solution is to leave the assembly name off when serializing primitives. I don't know which option that would fit into

Was this page helpful?
0 / 5 - 0 ratings