我有一个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;
}
}
样品小提琴。