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.
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()
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With