How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?

前端 未结 9 1806
星月不相逢
星月不相逢 2020-11-21 05:07

I am trying to extend the JSON.net example given here http://james.newtonking.com/projects/json/help/CustomCreationConverter.html

I have another sub class deriving

9条回答
  •  名媛妹妹
    2020-11-21 05:52

    As another variation on Totem's known type solution, you can use reflection to create a generic type resolver to avoid the need to use known type attributes.

    This uses a technique similar to Juval Lowy's GenericResolver for WCF.

    As long as your base class is abstract or an interface, the known types will be automatically determined rather than having to be decorated with known type attributes.

    In my own case I opted to use a $type property to designate type in my json object rather than try to determine it from the properties, though you could borrow from other solutions here to use property based determination.

     public class JsonKnownTypeConverter : JsonConverter
    {
        public IEnumerable KnownTypes { get; set; }
    
        public JsonKnownTypeConverter() : this(ReflectTypes())
        {
    
        }
        public JsonKnownTypeConverter(IEnumerable knownTypes)
        {
            KnownTypes = knownTypes;
        }
    
        protected object Create(Type objectType, JObject jObject)
        {
            if (jObject["$type"] != null)
            {
                string typeName = jObject["$type"].ToString();
                return Activator.CreateInstance(KnownTypes.First(x => typeName == x.Name));
            }
            else
            {
                return Activator.CreateInstance(objectType);
            }
            throw new InvalidOperationException("No supported type");
        }
    
        public override bool CanConvert(Type objectType)
        {
            if (KnownTypes == null)
                return false;
    
            return (objectType.IsInterface || objectType.IsAbstract) && KnownTypes.Any(objectType.IsAssignableFrom);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            // Load JObject from stream
            JObject jObject = JObject.Load(reader);
    
            // Create target object based on JObject
            var target = Create(objectType, jObject);
            // Populate the object properties
            serializer.Populate(jObject.CreateReader(), target);
            return target;
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    
        //Static helpers
        static Assembly CallingAssembly = Assembly.GetCallingAssembly();
    
        static Type[] ReflectTypes()
        {
            List types = new List();
            var referencedAssemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
            foreach (var assemblyName in referencedAssemblies)
            {
                Assembly assembly = Assembly.Load(assemblyName);
                Type[] typesInReferencedAssembly = GetTypes(assembly);
                types.AddRange(typesInReferencedAssembly);
            }
    
            return types.ToArray();
        }
    
        static Type[] GetTypes(Assembly assembly, bool publicOnly = true)
        {
            Type[] allTypes = assembly.GetTypes();
    
            List types = new List();
    
            foreach (Type type in allTypes)
            {
                if (type.IsEnum == false &&
                   type.IsInterface == false &&
                   type.IsGenericTypeDefinition == false)
                {
                    if (publicOnly == true && type.IsPublic == false)
                    {
                        if (type.IsNested == false)
                        {
                            continue;
                        }
                        if (type.IsNestedPrivate == true)
                        {
                            continue;
                        }
                    }
                    types.Add(type);
                }
            }
            return types.ToArray();
        }
    

    It can then be installed as a formatter

    GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new JsonKnownTypeConverter());
    

提交回复
热议问题