.NET 7:条件式 JSON 序列化/反序列化

huangapple go评论65阅读模式
英文:

.NET 7: Conditional Json serialization/deserialization

问题

我在.NET 7的API上工作,关于DTO对象的Id字段有问题。

这是我的UserDTO(用于在GET中返回用户以及在POST/PUT用户时使用):

public class UserDTO
{
  public string Id { get; set; }
  public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List<RoleDTO> Roles { get; set; } = new List<RoleDTO>();

  public bool ShouldSerializeId()
  {
    return false;
  }
}

UsersController中的Post:

[HttpPost]
[Route("users")]
public async Task<IActionResult> CreateUserAsync(UserDTO u)
{
    ...
}

Get:

[HttpGet("user/{userId}")]
public async Task<IActionResult> GetUserByIdAsync(string userId)
{
    var u = await _usersApplication.GetByIdAsync(userId);
    var user = _mapper.Map<UserDTO>(u);
    return Ok(user);
}

问题是,保持类这样,Swagger呈现的Post请求的JSON如下:

{
  "id": "string",
  "email": "string",
  "password": "string",
  "firstName": "string",
  "lastName": "string",
  "status": true,
  "roles": [
    {
      "roleName": "string"
    }
  ]
}

当然,我不希望“Id”在请求中以这种方式出现,因为它显然是自动生成的。

如果我使用[JsonIgnore]属性,它可以工作,但在Get中就不会显示“Id”,而那里显然是需要的。

我需要的是一种有条件的序列化/反序列化。

我尝试了“ShouldSerializeId”方法,但据我所读,它在.NET 7中不再起作用,因为这个版本的.NET带有一个不支持它的不同Json序列化器。

有没有办法进行条件序列化?

英文:

I'm working on a .NET 7 api and I'm having a problem regarding Id field of the DTO object.

This is my UserDTO (used to return users in get as well as to post/put a user):

public class UserDTO
{
  public string Id { get; set; }
  public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List&lt;RoleDTO&gt; Roles { get; set; } = new List&lt;RoleDTO&gt;();

  public bool ShouldSerializeId()
  {
    return false;
  }
}

UsersController Post:

[HttpPost]
[Route(&quot;users&quot;)]
public async Task&lt;IActionResult&gt; CreateUserAsync(UserDTO u)
{
    ...
}

Get:

[HttpGet(&quot;user/{userId}&quot;)]
public async Task&lt;IActionResult&gt; GetUserByIdAsync(string userId)
{
    var u = await _usersApplication.GetByIdAsync(userId);
    var user = _mapper.Map&lt;UserDTO&gt;(u);
    return Ok(user);
}

My problem is leaving the class like that, this next is the json presented by swagger as a Post request:

{
  &quot;id&quot;: &quot;string&quot;,
  &quot;email&quot;: &quot;string&quot;,
  &quot;password&quot;: &quot;string&quot;,
  &quot;firstName&quot;: &quot;string&quot;,
  &quot;lastName&quot;: &quot;string&quot;,
  &quot;status&quot;: true,
  &quot;roles&quot;: [
    {
      &quot;roleName&quot;: &quot;string&quot;
    }
  ]
}

And, of course, I don't want "Id" to be present in the request as it is, obviously, auto-generated.

If I use the [JsonIgnore] attribute it works, but then the "Id" won't be presented in Get, where it is obviously needed.

What I need is a conditional serializer/deserializer.

I tried the "ShouldSerializeId" approach, but as I've read it does not work anymore in .Net 7, as this version of .Net comes with a different Json serializer that does not support it.

Any help to do a conditional serialization?

答案1

得分: 1

如果你完全不想显示 Id 属性,唯一的方法就是将你的 DTO 分成两个

public class UserDTO:UserDtoBase
{
  public string Id { get; set; }
}

public class UserDTOBase
{
   public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List<RoleDTO> Roles { get; set; } = new List<RoleDTO>();

}

并在提交时使用不带 Id 的版本。

使用 JsonIgnore 或自定义序列化器与在 POST 操作内部赋值 Id = null 是一样的,因为将创建一个新的 DTO,只是 Id 不会被赋值,而会默认为 null。

英文:

If you don't want to show Id property at all , the only way is to separate your DTO into two

public class UserDTO:UserDtoBase
{
  public string Id { get; set; }
}

public class UserDTOBase
{
   public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List&lt;RoleDTO&gt; Roles { get; set; } = new List&lt;RoleDTO&gt;();

}

and use without Id for post

Using JsonIgnore or the custom serializer is the same as just assign Id = null inside of the POST action since new Dto will be created, just Id will not be assigned and will be null by default.

答案2

得分: 0

以下是翻译好的代码部分:

简单的答案 - 使用不同的DTO来创建和返回数据

public class CreateUserDTO
{
  public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List<RoleDTO> Roles { get; set; } = new List<RoleDTO>();
}

可选地,您可以通过继承来共享UserDTOCreateUserDTO之间的基本属性集。

对于序列化部分(如果您确实需要,尽管在这种特定情况下这是过度工作),您可以利用在.NET 7中添加的System.Text.Json的合同自定义支持

class TestSe
{
public int Id { get; set; }

public string Data { get; set; }

[System.Text.Json.Serialization.JsonIgnore]
public bool ShouldSerializeId { get; set; } = true;

}

void SetShouldSerialize(JsonTypeInfo info)
{
if (info.Type == typeof(TestSe))
{
// 注意:这使用JSON属性名称,而不是类成员名称
var jsonPropertyInfo = info.Properties.Single(propertyInfo => propertyInfo.Name == "Id");
jsonPropertyInfo.ShouldSerialize = static (obj, val) => ((TestSe)obj).ShouldSerializeId;
}
}


和用法:
```csharp
var testSe = new TestSe
{
    Data = "data"
};
var opts = new JsonSerializerOptions
{
    TypeInfoResolver = new DefaultJsonTypeInfoResolver
    {
        Modifiers =
        {
            SetShouldSerialize
        }
    }
};

Console.WriteLine(JsonSerializer.Serialize(testSe, opts)); // {"Id":0,"Data":"data"}
testSe.ShouldSerializeId = false;
Console.WriteLine(JsonSerializer.Serialize(testSe, opts)); // {"Data":"data"}

<details>
<summary>英文:</summary>

Simple answer - use different DTOs to create and return data:

```csharp
public class CreateUserDTO
{
  public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List&lt;RoleDTO&gt; Roles { get; set; } = new List&lt;RoleDTO&gt;();
}

Optionally you can share the base set of properties between UserDTO and CreateUserDTO via inheritance.

For serialization part (if you really need, though in this particular case it is an overkill) you can leverage contract customization support added in .NET 7 to System.Text.Json:

class TestSe
{
    public int Id { get; set; }

    public string Data { get; set; }

    [System.Text.Json.Serialization.JsonIgnore]
    public bool ShouldSerializeId { get; set; } = true;
}

void SetShouldSerialize(JsonTypeInfo info)
{
    if (info.Type == typeof(TestSe))
    {
        // NB: this uses JSON property name, not a class member name
        var jsonPropertyInfo = info.Properties.Single(propertyInfo =&gt; propertyInfo.Name == &quot;Id&quot;);
        jsonPropertyInfo.ShouldSerialize = static (obj, val) =&gt; ((TestSe)obj).ShouldSerializeId;
    }
}

And usage:

var testSe = new TestSe
{
    Data = &quot;data&quot;
};
var opts = new JsonSerializerOptions
{
    TypeInfoResolver = new DefaultJsonTypeInfoResolver
    {
        Modifiers =
        {
            SetShouldSerialize
        }
    }
};

Console.WriteLine(JsonSerializer.Serialize(testSe, opts)); // {&quot;Id&quot;:0,&quot;Data&quot;:&quot;data&quot;}
testSe.ShouldSerializeId = false;
Console.WriteLine(JsonSerializer.Serialize(testSe, opts)); // {&quot;Data&quot;:&quot;data&quot;}

答案3

得分: 0

你尝试过使用[JsonIgnore]装饰器和Newtonsoft.NET JSONConverter吗?

public class UserDTO
{
  [JsonIgnore]
  public string Id { get; set; }
  public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List<RoleDTO> Roles { get; set; } = new List<RoleDTO>();

  public bool ShouldSerializeId()
  {
    return false;
  }
}
英文:

Have you tried using the [JsonIgnore] decorator and Newtonsoft.NET JSONConverter?

public class UserDTO
{
  [JsonIgnore]
  public string Id { get; set; }
  public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List&lt;RoleDTO&gt; Roles { get; set; } = new List&lt;RoleDTO&gt;();

  public bool ShouldSerializeId()
  {
    return false;
  }
}

答案4

得分: -1

将对象结构分成两个对象:一个模型将保存在数据库中(如果要添加数据,返回的对象也可以是DTO,例如“请求时间”),另一个用于创建新用户。

public class User // 这个将保存在数据库中
{
  [Key]
  public string Id { get; set; }
  public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List<RoleDTO> Roles { get; set; } = new List<RoleDTO>();
}

public class NewUserRequest // 这个将发送到您的端点
{
  public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List<RoleDTO> Roles { get; set; } = new List<RoleDTO>();
}
英文:

Separate your object-structure into two objects- one model you'll save in your database (The returned object might be a DTO as well if you want to add data eg. "Time requested) and one for creating new Users.

public class User //This is the one you&#39;ll have in your database
{
  [Key]
  public string Id { get; set; }
  public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List&lt;RoleDTO&gt; Roles { get; set; } = new List&lt;RoleDTO&gt;();
}

public class NewUserRequest //This is the one you&#39;ll send to your endpoint
{
  public string Email { get; set; }
  public string Password { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public bool Status { get; set; } = true;
  public List&lt;RoleDTO&gt; Roles { get; set; } = new List&lt;RoleDTO&gt;();
}

huangapple
  • 本文由 发表于 2023年3月31日 18:43:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/75897620.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定