我有一个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;
答案 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。