Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serializing and deserializing in different frameworks #1378

Closed
Schwenkner opened this issue Jul 18, 2017 · 6 comments
Closed

Serializing and deserializing in different frameworks #1378

Schwenkner opened this issue Jul 18, 2017 · 6 comments

Comments

@Schwenkner
Copy link

Schwenkner commented Jul 18, 2017

I'm not sure, if this is a bug or just not implemented.

Having a class with a property of type "Type", once in a .Net framework and once in a UWP framework.
The type serializes with the AssemblyQualifiedName property of the type.

Thus serializing in the .Net framework rusults in something like
"System.EventArgs, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

If I deserialize this in an UWP application with JsonConver.DeserializeObject a propper Type object is created. Serializing this one in UWP, the following string is produced
"System.EventArgs, System.Private.CoreLib, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = 7cec85d7bea7798e"

Trying to deserialize this back in a .Net framework application, I get the error
"Error converting value "System.EventArgs, System.Private.CoreLib, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = 7cec85d7bea7798e" to type 'System.Type'."

The message is clear to me, but then I wouldn't expect why it works in the other direction. Is this due to tha fact, the UWP version is newer and can handle this convertion or is it a bug?

@TylerBrinkley
Copy link
Contributor

TylerBrinkley commented Jul 18, 2017

Serializing between different frameworks with the type name is unfortunately a very difficult problem. Just a quick look at https://apisof.net/catalog/System.EventArgs shows that System.EventArgs is defined in several different assemblies and even some of those are type-forwarded to private assemblies such as System.Private.CoreLib. The reason the UWP application can accept "System.EventArgs, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" is because there's an mscorlib facade assembly that type-forwards all of its types to the proper assemblies for UWP.

I'm really not sure what solution there can be for this. Perhaps a library could be created to map the various system type names to it's proper type for the runtime framework and the SerializationBinder could reference that library.

@JamesNK
Copy link
Owner

JamesNK commented Jul 18, 2017

Implement your own ISerializationBinder. With it you can customize the generatation and handling of the type name.

@arthurvaverko-kaltura
Copy link

arthurvaverko-kaltura commented Nov 21, 2019

For large code-base that vannot be easily converted to a custom ISerializationBinder
I have implement a redirect (not pretty but it works)

RedirectAssembly("System.Private.CoreLib", "mscorlib");

public static void RedirectAssembly(string fromAssemblyShotName, string replacmentAssemblyShortName)
{
    Console.WriteLine($"Adding custom resolver redirect rule form:{fromAssemblyShotName}, to:{replacmentAssemblyShortName}");
    ResolveEventHandler handler = null;
    handler = (sender, args) =>
    {
        // Use latest strong name & version when trying to load SDK assemblies
        var requestedAssembly = new AssemblyName(args.Name);
        Console.WriteLine($"RedirectAssembly >  requesting:{requestedAssembly}; replacment from:{fromAssemblyShotName}, to:{replacmentAssemblyShortName}");
        if (requestedAssembly.Name == fromAssemblyShotName)
        {
            try
            {
                Console.WriteLine($"Redirecting Assembly {fromAssemblyShotName} to: {replacmentAssemblyShortName}");
                var replacmentAssembly = Assembly.Load(replacmentAssemblyShortName);
                return replacmentAssembly;
            }
            catch (Exception e)
            {
                Console.WriteLine($"ERROR while trying to provide replacement Assembly {fromAssemblyShotName} to: {replacmentAssemblyShortName}");
                Console.WriteLine(e);
                return null;
            }
        }

        Console.WriteLine($"Framework faild to find {requestedAssembly}, trying to provide replacment from:{fromAssemblyShotName}, to:{replacmentAssemblyShortName}");

        return null;
    };

    AppDomain.CurrentDomain.AssemblyResolve += handler;
}

@TheXenocide
Copy link

TheXenocide commented Jan 21, 2021

To my knowledge, every serializer in the .NET BCL automatically adjusts type names to account for the TypeForwardedFromAttribute which appears to be the primary purpose for its existence (TypeForwardedToAttribute is used by the runtime itself, but this is the only purpose I've seen for the reverse lookup functionality). I realize you've already closed this ticket @JamesNK, but is there no chance to include this functionality in JSON.NET as well? It seems more like a miss than expected behavior, given the consistency of the other serializers.

As for anyone doing simple string replacement or the assembly resolve redirection above, it's possible for types in an assembly to be forwarded to multiple separate assemblies (and this does, in fact, happen with some types that have been reorganized in .NET Standard), so while it might work well enough for limited use cases, in some conditions it will not.

@andreycha
Copy link

andreycha commented Jul 29, 2021

One can extend DefaultSerializationBinder and use it in JsonSerializerSettings in .NET Framework application to deserialize JSON generated by .NET Core application:

internal sealed class DotNetCompatibleSerializationBinder : DefaultSerializationBinder
{
    private const string CoreLibAssembly = "System.Private.CoreLib";
    private const string MscorlibAssembly = "mscorlib";

    public override Type BindToType(string assemblyName, string typeName)
    {
        if (assemblyName == CoreLibAssembly)
        {
            assemblyName = MscorlibAssembly;
            typeName = typeName.Replace(CoreLibAssembly, MscorlibAssembly);
        }

        return base.BindToType(assemblyName, typeName);
    }
}

and then:

var settings = new JsonSerializerSettings()
{
    SerializationBinder = new DotNetCompatibleSerializationBinder()
};

@ericstj
Copy link

ericstj commented Sep 16, 2021

These hacks that remap System.Private.Corelib to mscorlib will not work in all cases. Some types in System.Private.Corelib were in different assemblies in .NETFramework. One case:
https://github.com/dotnet/runtime/blob/82223e5835e907d145705ef929de5e39ddb20733/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Queue.cs#L23

The correct solution is to honor TypeForwardedFromAttribute and it has to be done at serialization time since that information isn't present at deserialization.

Also note that this isn't just a .NETCore problem. Similar moves happened in .NETFramework https://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/TypeForwardedFromAttribute.cs,a0ffa0ad2414acf4,references

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants