Getting error resolving type specified in JSON

Viewed 2815

I am using newtownsoft JSON and I am receiving the following error:

Error resolving type specified in JSON 'CharacterData, WorldServer'. Path 'data.1.$type', line 1, position 67.

When deserializing like this:

  JsonData receivedData = JsonConvert.DeserializeObject<JsonData>(sdata, new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Objects,
        TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
    });

The part Json which is forcing this error:

{"header":"0x004","data":{"1":{"$type":"CharacterData, WorldServer","characterId":1,"connectionId":0,"accountId":3,"name":"Riorage",

Here is how I serialize it:

string JSonData = JsonConvert.SerializeObject(SendData, Formatting.None, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Auto
});

How can I fix that error? I would be great if when deserializing it does not care about , WorldServer. The Important aspect is CharacterData.

Note that I am serializing and deserializing in two different applications sending information to each other with TCP. Both are having exactly the same CharacterData class used for structure.

1 Answers

You can use a Custom ISerializationBinder to specify a custom $type value during serialization, and also to process the same custom $type value during deserialization.

That is to say, instead of:

"$type":"CharacterData, WorldServer"

We can serialize to:

"$type":"CharacterData, Global"

And can convert this at the receiving end to the known CharacterData type.

  • Please note that, when using TypeNameHandling = TypeNameHandling.Auto, our binder will be invoked only when the type of the object being serialized is not the same as its declared type. I think this is already the case for you so there should be no problems in this regard.

  • Please let me remind you that this implementation must be coded on both ends.

To make the proof of concept match your case; I've improvised the following hierarchy, where Transmission is the object being transmitted.

public class Transmission
{
    public TransmissionData Data { get; set; }
}

public class TransmissionData
{

}
public class CharacterData : TransmissionData
{
    public string CharacterId { get; set; }
}

Here is a sample converter:

public class CharacterDataSerializationBinder : ISerializationBinder
{
    DefaultSerializationBinder defaultBinder = new DefaultSerializationBinder();

    void ISerializationBinder.BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        if (serializedType == typeof(CharacterData))
        {
            assemblyName = "Global";
            typeName = serializedType.Name;
        }
        else
        {
            defaultBinder.BindToName(serializedType, out assemblyName, out typeName);
        }
    }

    Type ISerializationBinder.BindToType(string assemblyName, string typeName)
    {
        if (typeName == typeof(CharacterData).Name && assemblyName == "Global")
        {
            return typeof(CharacterData);
        }
        else
        {
            return defaultBinder.BindToType(assemblyName, typeName);
        }
    }
}

And here is how to use it:

    Transmission SendData = new Transmission()
    {
        Data = new CharacterData() { CharacterId = "454" }
    };

    string JSonData = JsonConvert.SerializeObject(SendData, Formatting.None, new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto,
        SerializationBinder = new CharacterDataSerializationBinder()
    });

    Transmission deserializedJson = JsonConvert.DeserializeObject<Transmission>(JSonData, new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto,
        SerializationBinder = new CharacterDataSerializationBinder()
    });

And here is the serialization output:

{"Data":{"$type":"CharacterData, Global","CharacterId":"454"}}

Hope this helps.

UPDATE 1: The OP asked whether they could use System.Runtime.Serialization.SerializationBinder instead of Json.Net's ISerializationBinder

The answer is yes. Json.Net supports this type of binder along with its own.

Here is a sample implementation of this kind of a binder:

public class CharacterDataCoreBinder : SerializationBinder
{
    public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        typeName = serializedType.FullName;

        if (serializedType == typeof(CharacterData))
        {
            assemblyName = "Global";
        }
        else
        {
            assemblyName = serializedType.Assembly.GetName().Name;
        }
    }

    public override Type BindToType(string assemblyName, string typeName)
    {
        if (typeName == typeof(CharacterData).FullName && assemblyName == "Global")
        {
            return typeof(CharacterData);
        }
        else
        {
            return Type.GetType(typeName + ", " + assemblyName);
        }
    }
}

And here is how to use it when serializing/deserializing with Json.Net:

    string JSonData = JsonConvert.SerializeObject(SendData, Formatting.None, new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto,
        // SerializationBinder = new CharacterDataSerializationBinder()
        Binder = new CharacterDataCoreBinder()
    });

    Transmission deserializedJson = JsonConvert.DeserializeObject<Transmission>(JSonData, new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto,
        // SerializationBinder = new CharacterDataSerializationBinder()
        Binder = new CharacterDataCoreBinder()
    });
Related