2

We have a situation where one on the fields that is being serialized and deserialized via json.net is a binding list property on one of our objects. when attempting to deserialize this field, we get an exception:

An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll

Additional information: Error resolving type specified in JSON 'WpfApplication1.Wrapper1[[System.ComponentModel.BindingList1[[System.String, mscorlib]], System]], WpfApplication1'. Path 'Potato.$type', line 4, position 131.

To reproduce, I've created a small sample:

public class ClassToSerialize
{
    public Wrapper<BindingList<string>> Potato { get; set; }
}

public class Wrapper<T>
{
    public Wrapper()
    {
    }

    public Wrapper(T item)
    {
        Value = item;
    }

    #region Properties

    [JsonProperty]
    public T Value { get; set; }

    #endregion
}

And the test is:

var objectToSerialize = new ClassToSerialize
{
    Potato = new Wrapper<BindingList<string>>(new BindingList<string>
    {
        "tomato",
        "basil"
    })
};

string serializedPotato = JsonSerializer<ClassToSerialize>.Serialize(objectToSerialize, true);
ClassToSerialize deserializedPotato = JsonSerializer<ClassToSerialize>.Deserialize(serializedPotato);

Where the serialization code is simply:

public class JsonSerializer<T> where T : class
{
    public static string Serialize(T item, bool isComplexType = false)
    {
        if (isComplexType)
        {
            string serializedJson = JsonConvert.SerializeObject(item, Formatting.Indented, new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Objects,
                TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
            });

            return serializedJson;
        }

        return JsonConvert.SerializeObject(item);
    }

    public static T Deserialize(string serializedItem)
    {
        var deserializedObject = JsonConvert.DeserializeObject<T>(serializedItem, new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Objects
        });

        return deserializedObject;
    }
}

The error occurs here: ClassToSerialize deserializedPotato = JsonSerializer<ClassToSerialize>.Deserialize(serializedPotato); but if I change the type of the underlying collection from BindingList<T> to List<T>, it all works fine.

Does anyone know what the problem here is and how to solve it?

Please note I've tested an unwrapped BindingList<> (i.e. not wrapped in another type) and that works fine.

Many thanks,

Community
  • 1
  • 1
kha
  • 19,123
  • 9
  • 34
  • 67
  • 1
    Seems like a problem with `System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple`. If I change it to `FormatterAssemblyStyle.Full` the problem goes away. With the simple type name, the call to `Type.GetType()` inside [`DefaultSerializationBinder.GetTypeFromTypeNameKey()`](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs) fails. not sure why; perhaps you should [report an issue](https://github.com/JamesNK/Newtonsoft.Json/issues)? – dbc Jan 18 '16 at 23:31
  • 1
    @dbc Thanks. That solved the issue but it's slightly less than optimal. I'll report an issue with this as you suggested. Thank you very much and if you write your comment as an answer, I'll happily accept it. – kha Jan 19 '16 at 08:14

1 Answers1

2

Seems like a problem with using FormatterAssemblyStyle.Simple. If I modify your code to use FormatterAssemblyStyle.Full the problem goes away.

Using the simple assembly name (in Json.NET 8.0.2), the call to assembly.GetType(typeName) inside DefaultSerializationBinder.GetTypeFromTypeNameKey(TypeNameKey typeNameKey) fails, even though the assembly is correct.

I'm not entirely sure why the call fails, since the simple assembly name is present for all generic argument types. The documentation for Assembly.GetType(string) doesn't specify how assemblies referenced by generic type names are discovered. The documentation for Type.GetType(string) states:

GetType only works on assemblies loaded from disk.

And later:

If typeName includes the namespace but not the assembly name, this method searches only the calling object's assembly and Mscorlib.dll, in that order. If typeName is fully qualified with the partial or complete assembly name, this method searches in the specified assembly. If the assembly has a strong name, a complete assembly name is required.

So perhaps the problem is that BindingList<T> is not in mscorlib.dll (which gets special treatment) and not in a DLL loaded from disk (it's from System.dll which is in the GAC)?

I can reproduce this inconsistency manually. If I do:

Type.GetType("System.Collections.Generic.List`1[[System.String, mscorlib]],mscorlib")

it works. However,

Type.GetType("System.ComponentModel.BindingList`1[[System.String,mscorlib]],System")

Fails.

Perhaps you could report an issue?

dbc
  • 104,963
  • 20
  • 228
  • 340