1

Closed

$type doesnt work if it does not proceed other properties on the type

description

if the $type property is used to support polymorphic properties, it will fail to work if it does not proceed other properties for the type. The exception given is "Could not create an instance of type Ixxxxxx. Type is an interface or abstract class and cannot be instantiated."
 
 
Linqpad script that demonstrates:
 
 
 
void Main()
{
var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.All;
string magicTypeString = typeof(Parent).FullName.Replace("Parent","{0}") + ", " + typeof(Parent).Assembly.GetName();
magicTypeString.Dump();
settings.Binder = new TypeNameSerializationBinder(magicTypeString); 
settings.NullValueHandling = NullValueHandling.Ignore;

////// when $type isnt first in list of other properties, fails.... 

var jsonWorks = @"
            {
                ""Child"": {
                    ""$type"": ""Foo"",
                    ""SomeProperty"": ""RandomValue""
                }
        }
";
var jsonFails = @"
            {
                ""Child"": {
                    ""SomeProperty"": ""RandomValue"",
                    ""$type"": ""Foo""
                }
        }
";
 
 JsonConvert.DeserializeObject<Parent>( jsonWorks, settings ).Dump("Works");  
 JsonConvert.DeserializeObject<Parent>( jsonFails, settings ).Dump("Fails");  // this will throw an exception
}
 
public class Parent
{
public ISomeInterface Child { get; set; }
}
 
public interface ISomeInterface
{
}
 
public class Foo : ISomeInterface
{
public string SomeProperty { get; set; }
}
 
public class Bar : ISomeInterface
{
public string SomeProperty { get; set; }
}
 
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)
{
string resolvedTypeName = string.Format(TypeFormat, typeName);

return Type.GetType(resolvedTypeName, true);
}
}
Closed May 4, 2012 at 1:01 AM by JamesNK
It has to be at the beginning because that is when the object is created.

comments

jatreflex wrote May 4, 2012 at 6:30 PM

That may be the way your code is written, but I'd like to point out that the JSON spec says object properties are unordered. And although most browsers tend to maintain a specific order, some other environments do not. I'm dealing with an issue right now caused by a client that does not guarantee a certain order to it's object properties, and this bug/design flaw.