4

I have a custom attribute [Foo] implemented as follows:

public class FooAttribute
  : Attribute
{
}

Now I want to use the System.Text.Json.JsonSerializer to step into each field that has that attribute, in order to manipulate how is serialized and deserialized.

For example, if I have the following class

class SampleInt
{
    [Foo] 
    public int Number { get; init; }

    public int StandardNumber { get; init; }

    public string Text { get; init; }
}

when I serialize an instance of this class, I want a custom int JsonConverter to apply only for that field.

public class IntJsonConverter
    : JsonConverter<int>
{
    public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // do whatever before reading if the text starts with "potato". But this should be triggered only if destination type has the Foo attribute. How?
        return reader.GetInt32();
    }

    public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
    {
        writer.WriteStringValue("potato" + value.ToString());
    }
}

so that the serialization for

var sample =
  new SampleInt
  {
    Number = 123,
    StandardNumber = 456
    Text = "bar"
};

like this

var serializeOptions = new JsonSerializerOptions();
var serializeOptions.Converters.Add(new IntJsonConverter());
var resultJson = JsonSerializer.Serialize(sample, serializeOptions);

results on the following json

{
  "number": "potato123",
  "standardNumber": 456,
  "text": "bar"
}

and not in

{
  "number": "potato123",
  "standardNumber": "potato456",
  "text": "bar"
}

In a similar manner, I want the deserialization to be conditional, and only use the custom converter if the destination field has the [Foo] attribute.

With Newtonsoft, this is possible using Contract Resolvers and overriding CreateProperties method like this.

public class SerializationContractResolver
    : DefaultContractResolver
{
    private readonly ICryptoTransform _encryptor;
    private readonly FieldEncryptionDecryption _fieldEncryptionDecryption;

    public SerializationContractResolver(
        ICryptoTransform encryptor,
        FieldEncryptionDecryption fieldEncryptionDecryption)
    {
        _encryptor = encryptor;
        _fieldEncryptionDecryption = fieldEncryptionDecryption;
        NamingStrategy = new CamelCaseNamingStrategy();
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);
        foreach (var jsonProperty in properties)
        {
            var hasAttribute = HasAttribute(type, jsonProperty);
            if (hasAttribute)
            {
                var serializationJsonConverter = new MyJsonConverter();
                jsonProperty.Converter = serializationJsonConverter;
            }
        }
        return properties;
    }
    
    private bool HasAttribute(Type type, JsonProperty jsonProperty)
    {
        var propertyInfo = type.GetProperty(jsonProperty.UnderlyingName);
        if (propertyInfo is null)
        {
            return false;
        }
        var hasAttribute =
            propertyInfo.CustomAttributes
                .Any(x => x.AttributeType == typeof(FooAttribute));
        var propertyType = propertyInfo.PropertyType;
        var isSimpleValue = propertyType.IsValueType || propertyType == typeof(string);
        var isSupportedField = isSimpleValue && hasPersonalDataAttribute;
        return isSupportedField;
    }
}

But I don't want to use Newtonsoft. I want to use the new dotnet System.Text.Json serializer. Is it possible to use it in a similar granular way?

dbc
  • 104,963
  • 20
  • 228
  • 340
diegosasw
  • 13,734
  • 16
  • 95
  • 159
  • 1
    https://stackoverflow.com/questions/64729381/defaultcontractresolver-equivalent-in-system-text-json and https://stackoverflow.com/questions/58926112/system-text-json-api-is-there-something-like-icontractresolver and https://github.com/dotnet/runtime/issues/36785 are relevant. – stuartd Aug 12 '21 at 15:04
  • Apparently on .net 7 :/ – Juan Carlos Nov 11 '22 at 11:54

0 Answers0