如何在反序列化期间以编程方式选择构造函数?
How to programmatically choose a constructor during deserialization?
我想反序列化按以下方式序列化的 System.Security.Claims.Claim
对象:
{
"Issuer" : "LOCAL AUTHORITY",
"OriginalIssuer" : "LOCAL AUTHORITY",
"Type" : "http://my.org/ws/2015/01/identity/claims/mytype",
"Value" : "myvalue",
"ValueType" : "http://www.w3.org/2001/XMLSchema#string"
}
我得到的是 JsonSerializationException
:
Unable to find a constructor to use for type
System.Security.Claims.Claim. A class should either have a default
constructor, one constructor with arguments or a constructor marked
with the JsonConstructor attribute.
经过一些调查,我终于理解了上面消息中 one 的含义: JSON 反序列化器无法找到正确的构造函数 - 在这种情况下Claim
类型 - 多个带参数的构造函数(尽管存在一个参数与上述属性完全匹配的构造函数)。
有没有办法告诉反序列化器选择哪个构造函数而不向该 mscorlib 类型添加 JsonConstructor
属性?
Daniel Halan 用 patch to Json.NET a few years ago 解决了这个问题。这些天有没有不修改Json.NET就能解决这个问题的方法?
如果无法将 [JsonConstructor]
属性添加到目标 class(因为您不拥有该代码),那么通常的解决方法是创建自定义 JsonConverter
正如@James Thorpe 在评论中所建议的那样。这非常简单。您可以将 JSON 加载到 JObject
中,然后从中选择单独的属性来实例化您的 Claim
实例。这是您需要的代码:
class ClaimConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(System.Security.Claims.Claim));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
string type = (string)jo["Type"];
string value = (string)jo["Value"];
string valueType = (string)jo["ValueType"];
string issuer = (string)jo["Issuer"];
string originalIssuer = (string)jo["OriginalIssuer"];
return new Claim(type, value, valueType, issuer, originalIssuer);
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
要使用转换器,只需将其实例传递给 JsonConvert.DeserializeObject<T>()
方法调用:
Claim claim = JsonConvert.DeserializeObject<Claim>(json, new ClaimConverter());
Fiddle: https://dotnetfiddle.net/7LjgGR
另一种至少适用于非密封 类 的方法是对其进行子类化,但仅使用您感兴趣的构造函数:
class MyClaim : Claim {
public MyClaim(string type, string value, string valueType, string issuer, string originalIssuer):
base(type, value, valueType, issuer, originalIssuer){}
}
然后您可以在没有帮助器的情况下反序列化到此对象 类,然后将其视为基本类型。
Claim claim = JsonConvert.DeserializeObject<MyClaim>(json);
对于密封的 类,您可以采用这种方法(假装 Claim
是密封的):
class MyClaim {
private Claim _claim;
public MyClaim(string type, string value, string valueType, string issuer, string originalIssuer) {
_claim = new Claim(type, value, valueType, issuer, originalIssuer);
}
public Claim Value { get {
return _claim;
}
}
}
Claim claim = JsonConvert.DeserializeObject<MyClaim>(json).Value;
ClaimConverter
已与 IdentityServer4 一起打包。
命名空间:IdentityServer4.Stores.Serialization
使用示例:
JsonConvert.DeserializeObject<T>(value, new IdentityServer4.Stores.Serialization.ClaimConverter());
感谢@chen-zhe,这是上述帖子中最简单的解决方案,包括已接受的答案。
就我而言,我需要反序列化整个客户端(Is4 模型),包括 Claims,它只是 Client 的子 class 之一。
这是我尝试过的并且效果很好的方法 -
[HttpPost]
public async Task<ActionResult<Client>> Post(Object model)
{
var clientString = model.ToString();
Client client = JsonConvert.DeserializeObject<Client>(clientString, new ClaimConverter());
}
完成这项工作的注意事项:
- 首先,在我的案例中,您可能想要取消框架的默认绑定(ASP.NET Core [FromBody] 属性和
- 在 IdentityServer4.Stores.Serialization 中将 ClaimCoverter 解析为本机 Id4 框架库 class 并且无需重新发明轮子来创建上面建议的自定义 JsonConverter 甚至 .NET Core Custom Model 绑定方法
我想反序列化按以下方式序列化的 System.Security.Claims.Claim
对象:
{
"Issuer" : "LOCAL AUTHORITY",
"OriginalIssuer" : "LOCAL AUTHORITY",
"Type" : "http://my.org/ws/2015/01/identity/claims/mytype",
"Value" : "myvalue",
"ValueType" : "http://www.w3.org/2001/XMLSchema#string"
}
我得到的是 JsonSerializationException
:
Unable to find a constructor to use for type System.Security.Claims.Claim. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.
经过一些调查,我终于理解了上面消息中 one 的含义: JSON 反序列化器无法找到正确的构造函数 - 在这种情况下Claim
类型 - 多个带参数的构造函数(尽管存在一个参数与上述属性完全匹配的构造函数)。
有没有办法告诉反序列化器选择哪个构造函数而不向该 mscorlib 类型添加 JsonConstructor
属性?
Daniel Halan 用 patch to Json.NET a few years ago 解决了这个问题。这些天有没有不修改Json.NET就能解决这个问题的方法?
如果无法将 [JsonConstructor]
属性添加到目标 class(因为您不拥有该代码),那么通常的解决方法是创建自定义 JsonConverter
正如@James Thorpe 在评论中所建议的那样。这非常简单。您可以将 JSON 加载到 JObject
中,然后从中选择单独的属性来实例化您的 Claim
实例。这是您需要的代码:
class ClaimConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(System.Security.Claims.Claim));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
string type = (string)jo["Type"];
string value = (string)jo["Value"];
string valueType = (string)jo["ValueType"];
string issuer = (string)jo["Issuer"];
string originalIssuer = (string)jo["OriginalIssuer"];
return new Claim(type, value, valueType, issuer, originalIssuer);
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
要使用转换器,只需将其实例传递给 JsonConvert.DeserializeObject<T>()
方法调用:
Claim claim = JsonConvert.DeserializeObject<Claim>(json, new ClaimConverter());
Fiddle: https://dotnetfiddle.net/7LjgGR
另一种至少适用于非密封 类 的方法是对其进行子类化,但仅使用您感兴趣的构造函数:
class MyClaim : Claim {
public MyClaim(string type, string value, string valueType, string issuer, string originalIssuer):
base(type, value, valueType, issuer, originalIssuer){}
}
然后您可以在没有帮助器的情况下反序列化到此对象 类,然后将其视为基本类型。
Claim claim = JsonConvert.DeserializeObject<MyClaim>(json);
对于密封的 类,您可以采用这种方法(假装 Claim
是密封的):
class MyClaim {
private Claim _claim;
public MyClaim(string type, string value, string valueType, string issuer, string originalIssuer) {
_claim = new Claim(type, value, valueType, issuer, originalIssuer);
}
public Claim Value { get {
return _claim;
}
}
}
Claim claim = JsonConvert.DeserializeObject<MyClaim>(json).Value;
ClaimConverter
已与 IdentityServer4 一起打包。
命名空间:IdentityServer4.Stores.Serialization
使用示例:
JsonConvert.DeserializeObject<T>(value, new IdentityServer4.Stores.Serialization.ClaimConverter());
感谢@chen-zhe,这是上述帖子中最简单的解决方案,包括已接受的答案。 就我而言,我需要反序列化整个客户端(Is4 模型),包括 Claims,它只是 Client 的子 class 之一。
这是我尝试过的并且效果很好的方法 -
[HttpPost]
public async Task<ActionResult<Client>> Post(Object model)
{
var clientString = model.ToString();
Client client = JsonConvert.DeserializeObject<Client>(clientString, new ClaimConverter());
}
完成这项工作的注意事项:
- 首先,在我的案例中,您可能想要取消框架的默认绑定(ASP.NET Core [FromBody] 属性和
- 在 IdentityServer4.Stores.Serialization 中将 ClaimCoverter 解析为本机 Id4 框架库 class 并且无需重新发明轮子来创建上面建议的自定义 JsonConverter 甚至 .NET Core Custom Model 绑定方法