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<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);
}
My problem is leaving the class like that, this next is the json presented by swagger as a Post request:
{
"id": "string",
"email": "string",
"password": "string",
"firstName": "string",
"lastName": "string",
"status": true,
"roles": [
{
"roleName": "string"
}
]
}
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 any more in .Net 7, as this version of .Net comes with a different Json serializer that does not support it.
How can I do a conditional serialization?
Simple answer - use different DTOs to create and return data:
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>();
}
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 => propertyInfo.Name == "Id");
jsonPropertyInfo.ShouldSerialize = static (obj, val) => ((TestSe)obj).ShouldSerializeId;
}
}
And usage:
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"}