json.net IValueProvider SetValue抛出异常丢失

时间:2017-01-16 07:03:21

标签: c# exception-handling json.net

我有一个CotractResolver,它返回每个DateTime属性的Time部分的属性(这样用户可以单独设置时间)。 我有一个TimeValueProvider,其SetValue方法如下:

public void SetValue(object target, object value)
{
    try
    {
        var time = value as string;
        var originalValue = _propertyInfo.GetValue(target);
        if (value == null)
        {
            _propertyInfo.SetValue(target, originalValue);
        }

        else if (string.IsNullOrWhiteSpace(time))
        {
            var originalDateTime = (DateTime?) originalValue ?? SqlDateTime.MinValue.Value;
            _propertyInfo.SetValue(target,
                new DateTime(originalDateTime.Year, originalDateTime.Month, originalDateTime.Day, 0, 0, 0));
        }
        else
        {
            var currentValue = GetCurrentValue(_propertyInfo.GetValue(target));
            var convertedDate = TimeSpan.Parse(time, new DateTimeFormatInfo {LongTimePattern = "HH:mm:ss"});
            var finalValue = new DateTime(currentValue.Year, currentValue.Month, currentValue.Day,
                convertedDate.Hours, convertedDate.Days, convertedDate.Seconds);
            _propertyInfo.SetValue(target, finalValue);
        }
    }
    catch (InvalidDataException)
    {
        throw new ValidationException(new[]
        {
            new ValidationError
            {
                ErrorMessage = "Time is not correct",
                FieldName = _propertyInfo.Name,
                TypeName = _propertyInfo.DeclaringType.FullName
            }
        });
    }
}

问题是每当我传递一个无效的数字时,例如99:99,TimeSpan.Parse引发异常,但是我没有把它放在这个方法之外,因此Json.Net反序列化了对象。 我已经删除了我的代码,无法找到导致此类行为的任何常规异常处理。 我在这里错过了合同解决方案和价值提供者的一些内容吗?

更新:这是我配置Json.net

的方式
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new EntityContractResolver();
                config.Formatters.JsonFormatter.SerializerSettings.ObjectCreationHandling = ObjectCreationHandling.Replace;

config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
              config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

1 个答案:

答案 0 :(得分:2)

您的问题是您正在尝试解析IValueProvider.SetValue()中的JSON字符串。但是,在JSON被反序列化后,值提供程序仅被称为 。其目的是在容器对象内设置反序列化值。因此,您当前的SetValue()方法实际上从未做过任何事情,因为:

  • 如果反序列化成功,则传入的object value将是DateTime而不是字符串。
  • 如果日期字符串无效,则根本不会调用该方法,因为已经抛出异常。

您需要做的是使用custom JsonConverter来解析JSON日期字符串并将其与现有值组合。 JsonConverter.ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)的参数existingValue包含属性的当前值,因此这很简单:

public class DateTimeConverter : JsonConverter
{
    public override bool CanWrite { get { return false; } }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DateTime) || objectType == typeof(DateTime?);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var token = JToken.Load(reader);
        if (token.Type == JTokenType.Date)
        {
            // Json.NET already parsed the date successfully.  Return it.
            return (DateTime)token;
        }
        else
        {
            TimeSpan span;
            if (token.Type == JTokenType.TimeSpan)
            {
                // Not sure this is actually implemented, see
                // http://stackoverflow.com/questions/13484540/how-to-parse-a-timespan-value-in-newtonsoft-json/13505910#13505910
                span = (TimeSpan)token;
            }
            else
            {
                var timeString = (string)token;
                if (String.IsNullOrWhiteSpace(timeString))
                    span = new TimeSpan();
                else
                {
                    try
                    {
                        span = TimeSpan.Parse(timeString, new DateTimeFormatInfo { LongTimePattern = "HH:mm:ss" });
                    }
                    catch (Exception ex)
                    {
                        throw new ValidationException(ex.Message);
                    }
                }
            }
            var currentValue = (DateTime?)existingValue ?? SqlDateTime.MinValue.Value;

            // Combine currentValue & TimeSpan and return.  REPLACE THIS WITH YOUR OWN LOGIC.
            // I don't really know how you want to do this.
            return new DateTime(currentValue.Year, currentValue.Month, currentValue.Day) + span;
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

然后将其应用于EntityContractResolver,如下所示:

public class EntityContractResolver : DefaultContractResolver
{
    DateTimeConverter converter = null;

    DateTimeConverter Converter
    {
        get
        {
            if (converter == null)
                converter = Interlocked.CompareExchange(ref converter, new DateTimeConverter(), null);
            return converter;
        }
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var jProperty = base.CreateProperty(member, memberSerialization);
        if (jProperty.PropertyType == typeof(DateTime) || jProperty.PropertyType == typeof(DateTime?))
        {
            jProperty.Converter = jProperty.MemberConverter = Converter;
        }
        return jProperty;
    }
}

示例fiddle