2017-01-16 85 views
1

我有一個CotractResolver返回屬性爲每個DateTime屬性的時間部分(以便用戶可以分別設置時間)。 我有一個TimeValueProvider具有SetValue方法如下:json.net IValueProvider 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; 
+0

請問您可以將問題擴展到完整[mcve]嗎?我試圖在控制檯應用程序中重現您的問題,但無法查看https://dotnetfiddle.net/Edraqz。我所觀察到的是您的'TimeValueProvider.SetValue()'方法未被調用,因爲Json.NET無法解析JSON字符串 - 並且只有在值被成功反序列化後才調用SetValue()。也許你想使用[自定義轉換器](http://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm)來代替? – dbc

回答

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

+0

感謝您的完整和準確的答案:) – Beatles1692