public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            // Newtonsoft doesn't call CanConvert if you specify the converter using a JsonConverter attribute
            // they just assume you know what you're doing :)
            if (!CanConvert(objectType))
            {
                // if there's no Unknown value, fall back to the StringEnumConverter behavior
                return(base.ReadJson(reader, objectType, existingValue, serializer));
            }

            if (reader.TokenType == JsonToken.Integer)
            {
                var intValue = Convert.ToInt32(reader.Value);
                var values   = (int[])Enum.GetValues(objectType);
                if (values.Contains(intValue))
                {
                    return(Enum.Parse(objectType, intValue.ToString()));
                }
            }

            if (reader.TokenType == JsonToken.String)
            {
                var stringValue = reader.Value.ToString();
                return(UnknownEnum.Parse(objectType, stringValue));
            }

            // we know there's an Unknown value because CanConvert returned true
            return(Enum.Parse(objectType, UnknownName));
        }
        public override object ReadJson(
            JsonReader reader,
            Type objectType,
            object existingValue,
            JsonSerializer serializer)
        {
            if (reader.TokenType != JsonToken.StartObject)
            {
                return(null);
            }

            var contract = serializer.ContractResolver.ResolveContract(objectType) as JsonObjectContract;

            if (contract == null)
            {
                return(existingValue);
            }

            // if objectType is one of our known types, we can ignore the type property
            T targetObject = Create(objectType);

            // read the data into a JObject so we can look at it
            var value = JObject.Load(reader);

            if (targetObject == null)
            {
                // use the Type property
                var typeProperty = contract.Properties.GetClosestMatchProperty("Type");
                if (typeProperty == null)
                {
                    // we don't know the type. just bail
                    return(existingValue);
                }

                if (!value.TryGetValue(typeProperty.PropertyName, StringComparison.OrdinalIgnoreCase, out var typeValue))
                {
                    // a type property exists on the contract, but the JObject has no value for it
                    return(existingValue);
                }

                var type = UnknownEnum.Parse <ArtifactType>(typeValue.ToString());
                targetObject = Create(type);
            }

            if (targetObject != null)
            {
                using (var objectReader = value.CreateReader())
                {
                    serializer.Populate(objectReader, targetObject);
                }
            }

            return(targetObject);
        }