用Property List <Claim>解析对象,并告诉JsonConvert.DeserializeObject使用特定的构造函数进行声明?

时间:2019-06-06 10:23:39

标签: c# json deserialization

我正在将.NET Core与Newtonsoft.Json一起使用。我有一个UserModel属性的List<Claim>

public class UserModel
{        
    public string GUID { get; set; }
    public bool isActive { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public List<Claim> Claims { get; set; }
}

,而我试图像这样将JSON请求解析为该对象类:

public IActionResult Testpost([FromBody]JObject body)
{
    if (body == null) return BadRequest();

    UserModel user = JsonConvert.DeserializeObject<UserModel>(body.ToString());

    return Ok(user);
}

但是将JSON反序列化为我无法访问的类似Claim类的对象会抛出异常

  

Newtonsoft.Json.JsonSerializationException:'无法找到用于类型System.Security.Claims.Claim的构造函数。一个类应该具有一个默认构造函数,一个带有参数的构造函数或一个标有JsonConstructor属性的构造函数。路径“索赔”

因为它不能决定构造函数

根据在线资源,我可以创建一个自定义转换器类,该类可以管理UserModel对象的创建,但是我想避免这种情况。

是否可以将JSON对象反序列化到我的UserModel类中,并告诉JsonConvert.DeserializeObject使用像Claim(String, String)这样的特定Claim构造函数来解析Claims?

编辑: 如@PaulG所述,我已经检查了How to programmatically choose a constructor during deserialization?

的答案

但是,使用的可接受解决方案创建一个新类,该类实现JsonConverter类,然后手动解析JObject请求的主体。而且,答案显示了如何处理Claims,而不是处理将Claims嵌套为属性的复杂对象

在线程中阅读另一种解决方案,它显示了如何创建一个类来直接实现所需的构造函数,如下所示:

class MyClaim : Claim {
public MyClaim(string type, string value):
    base(type, value){}
}

但是,这将要求我在编写代码时注意Claim和MyClaim之间的区别。 JSON转换器可能无法假定要使用哪个构造函数,但我应该能够告诉它哪个。还是它是设计使然,我必须为此专门吸收并编写额外的代码?

因为对我来说替代方法是这样的:

    public IActionResult CreatePublicUser([FromBody]JObject body)
    {

        string Username = body["Username"].ToString();
        string Password = body["Password"].ToString();

        var Claims = body["Claims"].Children();

        List<Claim> UserClaims = new List<Claim>();

        foreach (var c in Claims)
        {

            UserClaims.Add(
                new Claim(
                        c["Type"].ToString(), 
                        c["Value"].ToString()
                        )
                    );
        }

 UserModel NewUser = (new UserBuilder())
            .WithUserName(Username)
            .WithPassword(Password)
            .WithClaims(UserClaims)
            .Build();

 return Ok(NewUser)
}

1 个答案:

答案 0 :(得分:0)

我建议您完全采用另一种方法,这也是一种常见做法。定义仅用于与客户互动的模型,例如

public class UserModelWeb
{        
    public List<ClaimWeb> Claims { get; set; }
}

这些DTO对象将仅用于从JSON进行数据转换,而不会用于业务逻辑层。然后,您可以将Web模型映射到以后将要使用的业务逻辑。这将使您在读取外部数据时不必依赖内部类。即如果您突然添加了一个新字段,它将从外部(可能是不受信任的)源中填充。这种明确的关注点分离将不允许这样做,因为您将必须在Web模型中显式定义一个字段。

您的情况的示例:假设您稍后在数据库中将只有一个内部字段可以编辑:“ InternalNote”。如果您将该字段添加到模型中,则任何人都可以发布数据并编辑该字段,而您的意图只是允许您自己对其进行编辑。

此外,这将解决您的问题,因为您无需转换为其他类。

P.S。您可以在操作方法中直接使用您的类:

MyAction([FromBody]UserModelWeb user)

应该立即从json反序列化。