json.net IValueProvider SetValue 引发的异常丢失



我有一个CotractResolver,它返回每个DateTime属性的时间部分的属性(以便用户可以单独设置时间)。我有一个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;

您的问题是您正在尝试解析IValueProvider.SetValue()中的 JSON 字符串。 但是,只有在反序列化 JSON 后才会调用值提供程序。 其目的是在容器对象内设置反序列化值。 因此,您当前的SetValue()方法实际上从未执行任何操作,因为:

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

您需要做的是使用自定义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;
    }
}

样品小提琴。

相关内容

  • 没有找到相关文章

最新更新