0

I need to disable the default special handling of dictionaries and lists in json.net. I don't care about the prettiness of the json, I just need a properly functional serialization/deserialization mechanism. Specifically, the problems with the default behavior are that additional fields are not serialized if the type implements IEnumerable or IDictionary, and that dictionary keys are just ToString'd instead of properly serialized. I am replacing a BinaryFormatter in an infrastructure component which serializes a huge number of legacy objects, so refactoring all those objects to use mechanisms compatible with the default json.net behavior (attributes, typeconverters etc) would be not only a prohibitively huge undertaking, but I don't think it would even be possible in many cases. I've tried using a custom contract resolver like so:

    private class ContractResolver : DefaultContractResolver
    {
        protected override JsonContract CreateContract(Type objectType)
        {
            var contract = base.CreateContract(objectType);
            if (contract is JsonDictionaryContract || contract is JsonArrayContract)
            {
                if (typeof(ISerializable).IsAssignableFrom(objectType))
                    contract = CreateISerializableContract(objectType);
                else
                    contract = CreateObjectContract(objectType);
            }
            return contract;
        }
    }

I would expect that would effectively change those types back to using the standard object serialization behavior, but when I try to serialize a simple dictionary with this I get this error

System.ArgumentException : Invalid type owner for DynamicMethod.
at System.Reflection.Emit.DynamicMethod.Init(String name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] signature, Type owner, Module m, Boolean skipVisibility, Boolean transparentMethod)
at System.Reflection.Emit.DynamicMethod..ctor(String name, Type returnType, Type[] parameterTypes, Type owner, Boolean skipVisibility)
at Newtonsoft.Json.Utilities.DynamicReflectionDelegateFactory.CreateDynamicMethod(String name, Type returnType, Type[] parameterTypes, Type owner)
at Newtonsoft.Json.Utilities.DynamicReflectionDelegateFactory.CreateParameterizedConstructor(MethodBase method)
at Newtonsoft.Json.Serialization.JsonObjectContract.set_ParametrizedConstructor(ConstructorInfo value)
at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateObjectContract(Type objectType)

I'm not really sure how to go about addressing that :) Is there a better approach I could be taking? Any help is appreciated, Thanks!

Update: I realized in my above code that it did probably does make sense to maintain JsonArrayContract's for actual array types (as opposed to all IEnumerables), as it's not surprising that a JsonObjectContracts wouldn't necessarily be able to serialize a native array, so I changed the contract resolver like so:

if (contract is JsonDictionaryContract || (contract is JsonArrayContract && !objectType.IsArray))

which now produces this error instead, and I'm just as stumped as ever

Newtonsoft.Json.JsonSerializationException : Cannot preserve reference to array or readonly list, or list created from a non-default constructor: System.Collections.Generic.KeyValuePair`2[System.Int32,System.Int32][]. Path '$values', line 1, position 311.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadMetadataProperties(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue, Object& newValue, String& id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateISerializableItem(JToken token, Type type, JsonISerializableContract contract, JsonProperty member)
at Newtonsoft.Json.Serialization.JsonFormatterConverter.Convert(Object value, Type type)
at System.Runtime.Serialization.SerializationInfo.GetValue(String name, Type type)
at System.Collections.Generic.Dictionary`2.OnDeserialization(Object sender)
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
redec
  • 577
  • 2
  • 13
  • 1
    This has nothing to do with Dictionaries or Lists. This has to do with interfaces vs concrete types. – David L Sep 12 '16 at 15:06
  • I'm not sure what you mean by this, but the original error there seemed to be caused by an array. I don't see any special handling for interfaces in the base implementation of CreateContract... – redec Sep 12 '16 at 15:25
  • A JSON serialization stream is fundamentally different from a `BinaryFormatter` stream in that JSON primitives are weakly typed while `BinaryFormatter` primitives are strongly typed. To see what I mean, look at [How to (de)serialize a XmlException with Newtonsoft JSON?](https://stackoverflow.com/questions/35015357/how-to-deserialize-a-xmlexception-with-newtonsoft-json). To see if that matters here, I would need to see a [mcve] of a type that is failing serialization with this approach. – dbc Sep 15 '16 at 02:05
  • Thanks for that link, that is extremely informative. I'm not sure if that's what's happening here. Here's is the minimal implementation which I would expect to be able to successfully (de)serialize a dictionary via ISerializable: http://pastebin.com/yvuE9SZj. This program produces this error: http://pastebin.com/raw/AqK2TSzW – redec Sep 15 '16 at 17:18

0 Answers0