C# Newtonsoft 使用声明反序列化自定义对象?

Posted

技术标签:

【中文标题】C# Newtonsoft 使用声明反序列化自定义对象?【英文标题】:C# Newtonsoft Deserialize Custom Object with Claims? 【发布时间】:2020-12-17 10:32:13 【问题描述】:

我有以下简单的 POCO:

  public class ApiKey
  
    public ApiKey(string key, string owner, List<Claim> claims = null)
    
      Key = key;
      OwnerName = owner;
      Claims = claims ?? new List<Claim>();
    

    public string Key  get; 
    public string OwnerName  get; 
    public IReadOnlyCollection<Claim> Claims  get; set; 
  

我可以用一个声明创建这个对象的一个​​实例,然后用 Newtonsoft 序列化它:

JsonConvert.SerializeObject(key)

然后像这样得到一个序列化的 ApiKey:

"\"Key\":\"94a5b81f-9837-4c5f-9821-3ebaedc6435d\",\"OwnerName\":null,\"Claims\":[\"Issuer\":\"LOCAL AUTHORITY\",\"OriginalIssuer\":\"LOCAL AUTHORITY\",\"Properties\":,\"Subject\":null,\"Type\":\"AdminClaim\",\"Value\":\"AdminClaim\",\"ValueType\":\"http://www.w3.org/2001/XMLSchema#string\"]"

但是,如果尝试像这样使用 Newtonsoft 反序列化该字符串:

JsonConvert.DeserializeObject<ApiKey>(serialized_key);

我收到以下错误:

Newtonsoft.Json.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. Path 'Claims[0].Issuer', line 1, position 83.
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolvePropertyAndCreatorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObjectUsingCreatorWithParameters(JsonReader reader, JsonObjectContract contract, JsonProperty containerProperty, ObjectConstructor`1 creator, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)

当我希望取回我最初序列化的原始对象时。为了能够反序列化这个对象,我缺少什么?

谢谢!

编辑:Claim 对象来自 System.Security.Claims.Claim。

编辑 2:我希望能够序列化/反序列化整个 ApiKey 类,而不仅仅是声明部分。

编辑 3:我无法修改 ApiKey 类。

【问题讨论】:

错误很明显,对吧?仅仅因为一个类在没有合适的构造函数时是可序列化的,并不意味着没有合适的构造函数它是可反序列化的。这不是简单地复制一块内存的问题。 问题似乎是构造函数中的 Claim 类。如果您阅读了错误消息的第一行,则表明它 我正在使用 System.Security.Claims.Claim 类。知道如何让它反序列化吗? 【参考方案1】:

您必须编写一个自定义转换器,此答案中有一个 Claim 类的示例:https://***.com/a/28155770/6881299

【讨论】:

您必须使用 [JsonProperty(ItemConverterType = typeof(ClaimConverter))] 属性标记您的 IReadonlyCollection 属性。 另一种选择是使用 IdentityServer4 库中包含的 ClaimConverter,如下所述:***.com/a/54645939/10069673【参考方案2】:

问题是Claim 类没有任何公共构造函数。最简单的解决方法是创建自己的 Claim 类并将其反序列化:

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);

【讨论】:

以上是关于C# Newtonsoft 使用声明反序列化自定义对象?的主要内容,如果未能解决你的问题,请参考以下文章

JSON Newtonsoft C# 反序列化数组

c#开发中使用Newtonsoft.Json反序列化数组求解?

NewtonSoft.Json

C# Newtonsoft.Json 解析多嵌套json 进行反序列化

C# Newtonsoft JSON:反序列化和跳过对象名称

C# Newtonsoft.Json 解析多嵌套json 进行反序列化