2

Related question: JSON.NET - deserialize JSON into object, which class type is in inner field (but this one is specific to JSON.NET)

Is there a standard or recommended way to deserialise something like:

{
  "command" : "register_user",
  "params" : {
    "@c" : "register_params",
    "name" : "sdfsd",
    "email" : "sdfsd@ddkdk",
    "password" : "JDFffJJJd",
    "address" : {
       "postcode" : "12345",
       "street" : "cherry st",
       "number" : "44432",
       "country" : "antarctica"
    }
  }
}

If we told a deserializer to expect a Message type:

class Message
{
  string Command;
  object[] params;
}

There doesn't appear to be a JSON deserialization framework (or documentation within one), which can handle such a case.

  1. Perhaps there is one, or most, framework which simply scans the loaded types within the assembly to find the closest type?
  2. Perhaps within the JSON standard there is a standard field type for helping a deserializer select the right type?
  3. Perhaps there is no option but to include such helpers manually (both within the JSON string and in the deserializer logic)?

Thanks

UPDATE

For this particular example here, the implied use is for RPC. In my RPC solution, one needs to bind server objects to the RPC system, and the MethodInfo of each RPC method is cached - therefore the type of each parameter could indeed be fed into the JSON deserializer from the MethodInfo. Nevertheless, this question is still relevant, for example if the graph becomes deeper, with a more complex object then even this would not be enough. Furthermore, I'm sure this problem has/will been/be encountered outside of an RPC case. And all answers may be novel, I will simply pick the solution I believe is best.

Community
  • 1
  • 1
Kind Contributor
  • 17,547
  • 6
  • 53
  • 70

1 Answers1

1

You could use a custom SerializationBinder with JSON.NET and a $type property.

Let's take an example:

public class TypeNameSerializationBinder : SerializationBinder
{
    public string TypeFormat { get; private set; }

    public TypeNameSerializationBinder(string typeFormat)
    {
        TypeFormat = typeFormat;
    }

    public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        assemblyName = null;
        typeName = serializedType.Name;
    }

    public override Type BindToType(string assemblyName, string typeName)
    {
        var resolvedTypeName = string.Format(TypeFormat, typeName);
        return Type.GetType(resolvedTypeName, true);
    }
}

Now let's suppose that you have the following JSON:

{
  "command" : "register_user",
  "params" : {
    "$type" : "Person",
    "name" : "sdfsd",
    "email" : "sdfsd@ddkdk",
    "password" : "JDFffJJJd",
    "address" : {
       "postcode" : "12345",
       "street" : "cherry st",
       "number" : "44432",
       "country" : "antarctica"
    }
  }
}

and you have the following models inside a BarBaz assembly:

namespace FooBar
{
    public class Person
    {
        public string Name { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        public Address Address { get; set; }
    }

    public class Address
    {
        public string Postcode { get; set; }
        public string Street { get; set; }
        public string Number { get; set; }
        public string Country { get; set; }
    }
}

And a Message class:

public class Message
{
    public string Command { get; set; }
    public object Params { get; set; }
}

You could deserialize it like this:

var settings = new JsonSerializerSettings
{
    Binder = new TypeNameSerializationBinder("FooBar.{0}, BarBaz"),
    TypeNameHandling = TypeNameHandling.All
};
string json = ...
var message = JsonConvert.DeserializeObject<Message>(json, settings);
Debug.Assert(message.Params is Person);
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928