Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Proposal]: Add JsonPropertyFlag to System.Text.Json.Serialization to signal Property existence in Deserialize objects. #102186

Closed
CodeAngry opened this issue May 14, 2024 · 4 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Text.Json

Comments

@CodeAngry
Copy link

Background and motivation

Problem

Given the following JSONs:

{
   "name": null
}
{
}

...and this C# object:

class NameModel
{
  [JsonPropertyName("name")]
  public string? Name { get; set; }
}

When using JsonSerializer.Deserialize<NameModel>() on the input JSONs, they yield the (euphemistically) same object even if they are NOT the same object. One of them has a clearly specified NULL value while the other has it missing altogether, it's more like NONE.

Solution

What I propose is the addition of a new Attribute called JsonPropertyFlagAttribute that would work like this:

class NameModel
{
  [JsonPropertyName("name")]
  public string? Name { get; set; }

  [JsonPropertyFlag("name")]
  public bool HasName { get; set; } // or Name_ or w/e
  // must be a bool with any arbitrary name
}
  1. It would be used only when needed. It should NEVER be present where NULL and NONE don't need to be distinguished.
  2. Anything bearing the JsonPropertyFlag would be excluded from the actual (de)serialization process.
  3. [optional step] At the start of the deserialization process, all JsonPropertyFlag could be set to false.
  4. When a Property is read in the deserialization process, it should assign its value to the respective JsonPropertyName but should also signal its presence by toggling the JsonPropertyFlag to true. This would says: the Property is present. It may or may not have the NULL value but the presence itself is important.
  5. Since the flag is available, it could also be used when serializing to decide whether to actually print out a NULL value or just skip it. Just like JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull).

Inconveniences

Since this would be completely optional, it would change nothing in current JSON Api. But, when using this, the presence of the JsonPropertyFlag would also require of the JsonPropertyName counterpart and their .Name MUST match. This could be overcome by using the actual name of the linked property in the flag and resolving internally from there (and not the json literal property name), but it's an unneeded complication.

My Use Case

I really need this to do partial Database updates on tables with nullable fields. So if a field accepts NULL, I only update it when its Property is not missing since NULL is a valid value and I cannot differentiate whether it needs to be updated or not on its value alone.

API Proposal

namespace System.Text.Json.Serialization;

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class JsonPropertyFlagAttribute : JsonAttribute
{
	public JsonPropertyFlagAttribute(string name);
	public string Name { get; }
}

API Usage

class NameModel
{
  [JsonPropertyName("name")]
  public string? Name { get; set; }

  [JsonPropertyFlag("name")]
  public bool HasName { get; set; } // or Name_ or w/e
  // must be a bool with any arbitrary name
}

// presence (yet NULL) example
{
  var payload = JsonSerializer.Deserialize<NameModel>(@"{ ""name"": null }");
  if (payload.HasName) // == true
  {
    // ...
  }
}

// absence example
{
  var payload = JsonSerializer.Deserialize<NameModel>("{}");
  if (payload.HasName) // == false
  {
    // ...
  }
}

Alternative Designs

This same thing can be achieved by using JsonDocument or JsonNode but it's a VERY manual process in which you need to check for Property existence and also fetch it's Value, literally deserializing manually.

This could also be implemented with custom objects (like a JsonNullable<T> but should also handle references; and would have 3 states: none, null and value) types but more complicated than the provided minimal footprint solution.

Risks

I don't see any risks. It's an optional addition, very easy to operate around: don't ever serialize it, toggle when a property name matches. The JsonPropertyFlag would require the presence of the JsonPropertyName (so a little extra typing effort, only where needed) but it's optional.

@CodeAngry CodeAngry added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label May 14, 2024
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label May 14, 2024
@msackton
Copy link

I haven’t worked with System.Text.Json, but couldn’t you have the setter of the Name property set HasName to true? In most serialization/deserialization frameworks I’ve worked with the property setter is not called if the json string doesn’t contain a value and it is called with null if it contains a null value.

It is some boilerplate code but it seems like it keeps the solution at the level where it belongs (most users probably don’t care about this difference).

@CodeAngry
Copy link
Author

It's probably doable but would involve quite a bit of extra typing and having backing fields for every property But this would be simple and would require no changes to existing json models, except adding the flag field where needed.

@eiriktsarpalis
Copy link
Member

Support for such an attribute would be possible to implement using contract customization. It would require a bit of reflection correlating the two properties and then using a composite JsonPropertyInfo.Set delegate that updates both properties if a value is specified on the payload.

I don't think it's something we could consider as a built-in functionality though.

@eiriktsarpalis eiriktsarpalis closed this as not planned Won't fix, can't repro, duplicate, stale May 20, 2024
@dotnet-policy-service dotnet-policy-service bot removed the untriaged New issue has not been triaged by the area owner label May 20, 2024
@CodeAngry
Copy link
Author

@eiriktsarpalis Thanks! I just implemented it on my side.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Text.Json
Projects
None yet
Development

No branches or pull requests

3 participants