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

[System.Text.Json] Converter-level conditional serialization #36275

Closed
trinitrotoluene opened this issue May 12, 2020 · 2 comments
Closed

[System.Text.Json] Converter-level conditional serialization #36275

trinitrotoluene opened this issue May 12, 2020 · 2 comments

Comments

@trinitrotoluene
Copy link

trinitrotoluene commented May 12, 2020

Motivation

In many (usually web) scenarios, conditional serialization is an absolute must for a variety of reasons.

Some that come to mind:

  • Omitting certain fields in an API response for privacy (GDPR) reasons, avoiding duplication of types
  • Semantic difference between "foo": null and omitting "foo" entirely (for example RESTful PATCH calls)

Right now, STJ allows you to completely ignore null values as an options switch, which means this can be hacked in using a nullable struct and custom serializerfactory, but that's not a very reasonable API to expect users to be resorting to, and it results in juggling different JsonSerializerOptions around and polluting your data models with SomeWrapperType<T>).

It would be nice to be able to specify whether a custom or overriden type should be serialized at all at the converter-level.

API Proposal

Add an overridable JsonConverter<T>#ShouldSerialize(T, PropertyInfo, JsonSerializerOptions) method:

public abstract class JsonConverter<T>
{
    ...

    public virtual bool ShouldSerialize(T value, PropertyInfo propertyInfo, JsonSerializerOptions options) => true;

    ...
}

Which would be called before an attempt is made to write the property.

Performance Implications

This depends largely on the performance of the user-provided ShouldSerialize method, but by caching whether the method was overridden when initialising the property, the call could be ommitted for converters that don't implement it.

Usage

Example: the Option pattern (factory omitted):

public class OptionConverter<T> : JsonConverter<Option<T>>
{
    ...

    public override Option<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        ...
    }

    public override bool ShouldSerialize(Option<T> value, PropertyInfo propertyInfo, JsonSerializerOptions options)
    {
        return value.HasValue;
    }

    public override void Write(Utf8JsonWriter writer, Option<T> value, JsonSerializerOptions options)
    {
        ...
    }
}

One final note, the added propertyInfo parameter is for enabling masking logic based on custom attributes or property names, result of which the implementer could then cache per-property.

I think this would be a useful feature for many users and increase the versatility of the serializer. As for whether it's a feature that exists in JSON.NET - this kind of functionality would be implemented through the IContractResolver interface by providing a delegate to JsonProperty#ShouldSerialize when creating the property.

The IContractResolver API is a lot more expansive as it operates at the type-level and solves a lot more of the issues that I see users opening here, but I think that this is still a valid extension point to offer. (Issue tracking that is #31257)

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-System.Text.Json untriaged New issue has not been triaged by the area owner labels May 12, 2020
@layomia
Copy link
Contributor

layomia commented May 15, 2020

As mentioned, having a model similar to implementing an IContractResolver and adding API such as the Should(De)serialize predicates would also help this scenario. The APIs proposed in this issue would have to be examined alongside the design for such a model in System.Text.Json, to ensure that we have a cohesive set of APIs end to end. We don't want to end up having too many APIs to achieve the same result.

Related: #34456 (discusses types that would likely be exposed as part of an IContractResolver-like model)

@layomia layomia removed the untriaged New issue has not been triaged by the area owner label May 15, 2020
@layomia layomia added this to the Future milestone May 15, 2020
@eiriktsarpalis
Copy link
Member

Adding IContractResolver-like functionality is certainly on our radar (see #34456), but there have been separate proposals on controlling serialization on the converter level (see #55781). I'm going to close this issue in favor of the other two.

@ghost ghost locked as resolved and limited conversation to collaborators Nov 18, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants