Markus Weber
Markus Weber

Reputation: 1107

Json.net deserializing: Handle error, retry to deserialize the object which failed to deserialize and continue deserialization

My error handling fixes the deserialization problem itself of JsonConvert.DeserializeObject. My question is now, how can i retry to deserialize the problematic object during process without breaking the whole deserialization procedure and trying again?

At first i set my error handling as follows JsonSerializerSettings.Error = HandleCouldNotLoadAssemblyError;

The serializer class looks like this:

public class Serializer
{
    private JsonSerializerSettings Settings { get; set; }

    public Serializer()
    {
        Settings = new JsonSerializerSettings(); // todo: add converters
        Settings.TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full;
        Settings.TypeNameHandling = TypeNameHandling.Objects;
        Settings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;

        Settings.Error = HandleCouldNotLoadAssemblyError;
    }


    private void HandleCouldNotLoadAssemblyError(object sender, ErrorEventArgs args)
    {
        if (args != null && args.ErrorContext != null && args.ErrorContext.Error != null && args.ErrorContext.Error.GetType() == typeof(JsonSerializationException))
        {
            Logger.Error(args.ErrorContext.Error);
            foreach (var repository in DeviceInformationManager.InformationSources.OfType<DeviceDriverRepository.DeviceDriverRepository>())
            {
                var result = repository.LoadType(args.CurrentObject.GetType().FullName);
                if (result != null)
                {
                    //args.CurrentObject = JsonConvert.DeserializeObject("SUBSTRING OF SERIALIZED DATA");

                    args.ErrorContext.Handled = true;
                    break;
                }
            }
        }

    }

    public string Serialize(object obj)
    {
        // Serialize
        return JsonConvert.SerializeObject(obj, Formatting.Indented, Settings);
    }

    public T Deserialize<T>(string serializedData)
    {
        // Deserialize
        return JsonConvert.DeserializeObject<T>(serializedData, Settings);
    }

The reason for the fail of json.net is that my Assemblies are loaded dynamically during runtime. In the error handling I load missing assemblies.

At the moment deserialization only works by calling the Deserialize twice.

Upvotes: 0

Views: 2529

Answers (1)

Markus Weber
Markus Weber

Reputation: 1107

dbc led me on right track. So I coded a type binder to solve my problem:

    /// <summary>
    /// This binder looks for not found <see cref="Type"/>s in the unloaded assemblies of the repositories.
    /// </summary>
    public class RepositoryTypesBinder : DefaultSerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            Type type = null;
            try
            {
                type = base.BindToType(assemblyName, typeName);

                if (type != null)
                {
                    return type;
                }
            }
            catch (JsonSerializationException)
            {
                // Type has not been found, so try to find type in some repositories
                foreach (var repository in DeviceInformationManager.InformationSources.OfType<DeviceDriverRepository.DeviceDriverRepository>())
                {
                    // Search for assembly paths and check their names
                    foreach (var driverUri in repository.GetDriverUris(null))
                    {
                        var targetAssemblyName = AssemblyName.GetAssemblyName(driverUri.LocalPath);
                        if (targetAssemblyName != null && targetAssemblyName.FullName.Equals(assemblyName))
                        {
                            // Load assembly into AppDomain
                            Assembly assembly = Assembly.Load(targetAssemblyName);
                            type = assembly.GetType(typeName, false);
                            if (type != null)
                            {
                                return type; // If type was found: finish
                            }
                            // else continue
                        }
                    }
                }

                // If not sufficient type has found until here: throw Error;
                throw;
            }

            return null;
        }

        public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
        {
            assemblyName = serializedType.Assembly.FullName;
            typeName = serializedType.FullName;
        }
    }

Use it like this:

var Settings = new JsonSerializerSettings(); 
Settings.Binder = new RepositoryTypesBinder();
JsonConvert.DeserializeObject<T>(serializedData, Settings);

Upvotes: 1

Related Questions