How to change the Assemby and the Object type on t

2019-07-09 02:06发布

问题:

I've looked and tried every single solution posted here, with no avail.

My problem is:

  • On a web solution (ASP.NET MVC 3 C# / Razor), I'm using Json.Net to serialize the data displayed on some reports, to be able to send it over to a WPF application. These reports results are a collection of Model objects.

  • I have the same Model objects on the WPF application, so when I deserialize the Json string, I would like to bind the results accordingly (keeping the original Model object).

  • The Assembly name and Object type are different on each end (Web / App) - different namespaces.

Here's what I've tried so far:

On the web solution:

// MyModel
public class MyModel
{
    public long Id { get; set; }
    public string Name { get; set; }
}

...

// data = IEnumerable<MyModel>
var jsonData = JsonConvert.SerializeObject(data.ToArray(), data.ToArray().GetType(), 
    new JsonSerializerSettings 
    { 
        TypeNameHandling = TypeNameHandling.All
    });

On the app:

// MyModel
public class MyModel
{
    [JsonProperty("Id")]
    public long Id { get; set; }
    [JsonProperty("Name")]            
    public string Name { get; set; }
}

...

var jsonArray = JsonConvert.DeserializeObject(e.jsonObject,
    null,
    new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.All,
            Binder = new MySerializationBinder()
        });

...

public class MySerializationBinder : DefaultSerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        return typeof(MyModel);
    }
}

Can anyone give me a hand on this, please?

Thanks!

UPDATE

As per @Marc Gravell comment:

I forgot to mention the main issue here. I need to send the Object type across to the WPF app, because the listener will be expecting data from many reports - which are collections of different Models. So, when binding it back, I know which Object should be binded.

回答1:

I stand by my original answer - type information in serialization data is just really messy - it would be far better to change the code not to need this, but to "fix" it (not sure that is the right word) - look carefully at the typeName - it isn't always what you are expecting:

public class MySerializationBinder : DefaultSerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        switch(typeName)
        {
            case "WebSolution.MyModel[]": return typeof(Application.MyModel[]);
            case "WebSolution.MyModel": return typeof(Application.MyModel);
            default: return base.BindToType(assemblyName, typeName);
        }

    }
}

Incidentally, once the array type is known, the element type is implicit - so you can save some effort by only including the array type:

        new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Arrays
        }

But I still advise: don't do it this way.


The "mistake" here is including the full type names in the first place; the presence of type names in json should usually be the warning sign of a code-smell. Remove those, and there is nothing to do - it just works:

using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
static class Program
{
    static void Main()
    {
        // here imagine we're in the web tier, serializing
        var data = GetData();
        var jsonData = JsonConvert.SerializeObject(
            data.ToArray(), Formatting.None);
        // now imagine we're at the application, deserializing
        var appData = JsonConvert.DeserializeObject<Application.MyModel[]>(
            jsonData);
        // and it all works fine
    }
    static IEnumerable<WebSolution.MyModel> GetData()
    {
        yield return new WebSolution.MyModel { Id = 123, Name = "abc" };
        yield return new WebSolution.MyModel { Id = 456, Name = "def" };
    }
}

namespace WebSolution
{
    // MyModel
    public class MyModel
    {
        public long Id { get; set; }
        public string Name { get; set; }
    }
}

namespace Application
{
    // MyModel
    public class MyModel
    {
        [JsonProperty("Id")]
        public long Id { get; set; }
        [JsonProperty("Name")]
        public string Name { get; set; }
    }
}