ASP.NET WebApi 2:禁用包含 ISO8601 日期的字符串参数的反序列化
ASP.NET WebApi 2: Disable deserialization of string argument containing ISO8601 Date
我有一个 Asp.Net WebApi 控制器,具有以下操作:
public void Post([FromBody]object value)
现在,这个 value
参数有时作为包含 ISO8601 格式日期的 String
发送,有时作为 DateTime
发送。数据以 JSON 格式发送。
对于这两个选项中的每一个,我都必须做不同的事情,所以我需要做出区分,但我总是以 DateTime
值结束。
我知道我的源 DateTime
值在发送到上述操作时被序列化为 JSON 作为 ISO8601 格式的字符串(我正在使用 HttpClient
用于实际发送),因此我的操作无法区分两者。
我的问题是是否可以在发送的值中包含某种类型提示,以便告诉我的 WebApi 端点正在发送哪种特定类型(并在反序列化 value
上强制执行该类型).
最终通过实现自定义 JsonConverter
解决了这个问题,该自定义 JsonConverter
序列化字符串值和类型提示(类似于 Newtonsoft.Json
中 JsonSerializerSettings
中的 MetadataPropertyHandling
设置) .
下面是实现技巧的实现:
public class DateStringTypeConservingConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsAssignableFrom(typeof(string));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.ReadFrom(reader);
var typeMetadata = token["$type"];
if (typeMetadata?.Value<string>() == typeof(string).FullName)
{
return token.Value<string>("$value");
}
// Default behavior
return serializer.Deserialize(reader, objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is string)
{
WriteJsonStringWithType(writer, value as string);
}
else
{
// Default behavior
serializer.Serialize(writer, value);
}
}
// Write the given string to the given JSON writer with type info:
// { $type: "System.String", $value: "<string content>" }
private void WriteJsonStringWithType(JsonWriter writer, string value)
{
writer.WriteStartObject();
writer.WritePropertyName("$type");
writer.WriteValue(typeof(string).FullName);
writer.WritePropertyName("$value");
writer.WriteValue(value);
writer.WriteEndObject();
}
}
还有一点用法示例:
static void Main(string[] args)
{
var dateString = new Wrapper
{
Value = "2017-01-08T21:24:48.114Z"
};
var converters = new JsonConverter[] { new DateStringTypeConservingConverter() };
var serializedDateString = JsonConvert.SerializeObject(dateString, new JsonSerializerSettings
{
Converters = converters
});
var deserializedDateString =
JsonConvert.DeserializeObject<Wrapper>(serializedDateString, converters);
// Output: System.String
Console.WriteLine($"The type of deserialized value is: { deserializedDateString.Value.GetType() }");
Console.ReadKey();
}
class Wrapper
{
public object Value { get; set; }
}
我有一个 Asp.Net WebApi 控制器,具有以下操作:
public void Post([FromBody]object value)
现在,这个 value
参数有时作为包含 ISO8601 格式日期的 String
发送,有时作为 DateTime
发送。数据以 JSON 格式发送。
对于这两个选项中的每一个,我都必须做不同的事情,所以我需要做出区分,但我总是以 DateTime
值结束。
我知道我的源 DateTime
值在发送到上述操作时被序列化为 JSON 作为 ISO8601 格式的字符串(我正在使用 HttpClient
用于实际发送),因此我的操作无法区分两者。
我的问题是是否可以在发送的值中包含某种类型提示,以便告诉我的 WebApi 端点正在发送哪种特定类型(并在反序列化 value
上强制执行该类型).
最终通过实现自定义 JsonConverter
解决了这个问题,该自定义 JsonConverter
序列化字符串值和类型提示(类似于 Newtonsoft.Json
中 JsonSerializerSettings
中的 MetadataPropertyHandling
设置) .
下面是实现技巧的实现:
public class DateStringTypeConservingConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsAssignableFrom(typeof(string));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.ReadFrom(reader);
var typeMetadata = token["$type"];
if (typeMetadata?.Value<string>() == typeof(string).FullName)
{
return token.Value<string>("$value");
}
// Default behavior
return serializer.Deserialize(reader, objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is string)
{
WriteJsonStringWithType(writer, value as string);
}
else
{
// Default behavior
serializer.Serialize(writer, value);
}
}
// Write the given string to the given JSON writer with type info:
// { $type: "System.String", $value: "<string content>" }
private void WriteJsonStringWithType(JsonWriter writer, string value)
{
writer.WriteStartObject();
writer.WritePropertyName("$type");
writer.WriteValue(typeof(string).FullName);
writer.WritePropertyName("$value");
writer.WriteValue(value);
writer.WriteEndObject();
}
}
还有一点用法示例:
static void Main(string[] args)
{
var dateString = new Wrapper
{
Value = "2017-01-08T21:24:48.114Z"
};
var converters = new JsonConverter[] { new DateStringTypeConservingConverter() };
var serializedDateString = JsonConvert.SerializeObject(dateString, new JsonSerializerSettings
{
Converters = converters
});
var deserializedDateString =
JsonConvert.DeserializeObject<Wrapper>(serializedDateString, converters);
// Output: System.String
Console.WriteLine($"The type of deserialized value is: { deserializedDateString.Value.GetType() }");
Console.ReadKey();
}
class Wrapper
{
public object Value { get; set; }
}