lundi 26 octobre 2020

How can I know if an object is serializable when I don't staticly know the object's type?

I would like to make a snapshot of all the static non herited variable in all my assemblies at a given point in time in order to be able to restore them later on.

I have the CustomMemberInfo which store information of a static variable :

    public class CustomMemberInfo
    {
        public string name;
        public Type type;
        public object value;

        public CustomMemberInfo(string n, Type t, object v)
        {
            name = n;
            type = t;
            value = v;
        }
    }

And I try to serialize this static context like this :

private void saveStaticCache(string outputDirectory, JsonSerializerSettings settings)
        {
            if (ConfigurationManager.AppSettings["saveStaticState"] == "true")
            {
                AppDomain currentDomain = AppDomain.CurrentDomain;
                // Get all the loaded assemblies which are not installed in the global assembly cache. Means ignoring all System.XXX assemblies
                var assemblies = currentDomain.GetAssemblies().Where(assem => assem.GlobalAssemblyCache == false);


                List<CustomMemberInfo> statics = new List<CustomMemberInfo>();
                foreach (Assembly asm in assemblies)
                {
                    foreach (Type t in asm.GetTypes())
                    {
                        if (t.IsGenericType || t.IsGenericParameter | t.IsConstructedGenericType | t.IsGenericTypeDefinition)
                        {
                            continue;
                        }
                        
                        CustomMemberInfo[] staticFields = t
                            .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
                            .Where(field => field.IsNotSerialized == false && field != null && field.FieldType.IsGenericParameter == false && field.GetValue(null) != null)
                            .Select(field => new CustomMemberInfo(field.Name, field.FieldType, field.GetValue(null)))
                            .ToArray();
                        CustomMemberInfo[] staticProperties = t
                            .GetProperties(BindingFlags.Public | BindingFlags.Static |BindingFlags.DeclaredOnly)
                            .Where(property => property != null && property.PropertyType.IsGenericParameter == false && property.GetValue(null) != null)
                            .Select(property => new CustomMemberInfo(property.Name, property.PropertyType, property.GetValue(null)))
                            .ToArray();
                        
                        statics.AddRange(staticFields);
                        statics.AddRange(staticProperties);
                    }

                }

                string serializedStatics = JsonConvert.SerializeObject(statics, settings); // exception raised here
                File.WriteAllText(Path.Combine(outputDirectory, "statics.json"), serializedStatics);
            }
        }

When I call this method I get a :

Newtonsoft.Json.JsonSerializationException: 'Error getting value from 'MetadataDispenser' on 'PostSharp.Serialization.SerializationReader+InstanceFields'.'
Inner exception : NullREferenceException: Object reference not set to an instance of an object 

I imagine that a weird object is non serializable in the postsharp assembly and I would like to know how I can detect that in order to not add it to my CustomMemberInfo list.

Also, I use the postsharp WCF and NewtonSoft assemblies. I don't particularly want to save the static context in these assemblies but I don't see any criteria that will allow me to know that they do not directly belong in my solution. I can think of a solution using a whitelist of my assemblies name or a non-exhaustive blacklist of external assemblies (the white/black-list beeing read in a conf file).

EDIT : The null reference occurs in JsonConvert, not in my code. Here's the stackstrace :

at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
   at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
   at Newtonsoft.Json.JsonConvert.SerializeObjectInternal(Object value, Type type, JsonSerializer jsonSerializer)
   at Newtonsoft.Json.JsonConvert.SerializeObject(Object value, JsonSerializerSettings settings)
   at WcfExceptionHandlingPocLib.Aspects.MethodBoundaryAspect.saveStaticCache(String outputDirectory, JsonSerializerSettings settings) in [..]\MethodBoundaryAspect.cs:line 174




Aucun commentaire:

Enregistrer un commentaire