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

前端 未结 9 1807
星月不相逢
星月不相逢 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:55

    The above solution for the JsonCreationConverter is all over the internet, but has a flaw that manifests itself in rare occasions. The new JsonReader created in the ReadJson method does not inherit any of the original reader's configuration values (Culture, DateParseHandling, DateTimeZoneHandling, FloatParseHandling, etc...). These values should be copied over before using the new JsonReader in serializer.Populate().

    This is the best I could come up with to fix some of the problems with the above implementation, but I still think there are some things being overlooked:

    Update I updated this to have a more explicit method that makes a copy of an existing reader. This just encapsulates the process of copying over individual JsonReader settings. Ideally this function would be maintained in the Newtonsoft library itself, but for now, you can use the following:

    /// Creates a new reader for the specified jObject by copying the settings
    /// from an existing reader.
    /// The reader whose settings should be copied.
    /// The jToken to create a new reader for.
    /// The new disposable reader.
    public static JsonReader CopyReaderForObject(JsonReader reader, JToken jToken)
    {
        JsonReader jTokenReader = jToken.CreateReader();
        jTokenReader.Culture = reader.Culture;
        jTokenReader.DateFormatString = reader.DateFormatString;
        jTokenReader.DateParseHandling = reader.DateParseHandling;
        jTokenReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
        jTokenReader.FloatParseHandling = reader.FloatParseHandling;
        jTokenReader.MaxDepth = reader.MaxDepth;
        jTokenReader.SupportMultipleContent = reader.SupportMultipleContent;
        return jTokenReader;
    }
    

    This should be used as follows:

    public override object ReadJson(JsonReader reader,
                                    Type objectType,
                                    object existingValue,
                                    JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        // Load JObject from stream
        JObject jObject = JObject.Load(reader);
        // Create target object based on JObject
        T target = Create(objectType, jObject);
        // Populate the object properties
        using (JsonReader jObjectReader = CopyReaderForObject(reader, jObject))
        {
            serializer.Populate(jObjectReader, target);
        }
        return target;
    }
    

    Older solution follows:

    /// Base Generic JSON Converter that can help quickly define converters for specific types by automatically
    /// generating the CanConvert, ReadJson, and WriteJson methods, requiring the implementer only to define a strongly typed Create method.
    public abstract class JsonCreationConverter : JsonConverter
    {
        /// Create an instance of objectType, based properties in the JSON object
        /// type of object expected
        /// contents of JSON object that will be deserialized
        protected abstract T Create(Type objectType, JObject jObject);
    
        /// Determines if this converted is designed to deserialization to objects of the specified type.
        /// The target type for deserialization.
        /// True if the type is supported.
        public override bool CanConvert(Type objectType)
        {
            // FrameWork 4.5
            // return typeof(T).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
            // Otherwise
            return typeof(T).IsAssignableFrom(objectType);
        }
    
        /// Parses the json to the specified type.
        /// Newtonsoft.Json.JsonReader
        /// Target type.
        /// Ignored
        /// Newtonsoft.Json.JsonSerializer to use.
        /// Deserialized Object
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
                return null;
    
            // Load JObject from stream
            JObject jObject = JObject.Load(reader);
    
            // Create target object based on JObject
            T target = Create(objectType, jObject);
    
            //Create a new reader for this jObject, and set all properties to match the original reader.
            JsonReader jObjectReader = jObject.CreateReader();
            jObjectReader.Culture = reader.Culture;
            jObjectReader.DateParseHandling = reader.DateParseHandling;
            jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
            jObjectReader.FloatParseHandling = reader.FloatParseHandling;
    
            // Populate the object properties
            serializer.Populate(jObjectReader, target);
    
            return target;
        }
    
        /// Serializes to the specified type
        /// Newtonsoft.Json.JsonWriter
        /// Object to serialize.
        /// Newtonsoft.Json.JsonSerializer to use.
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }
    }
    

提交回复
热议问题