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?