I'd like to introduce some metadata properties into the json output for instances of a particular type when JSON.NET serializes the type.
What is the best way to introduce these additional properties while preserving the serialization context and settings?
I know that I can implement a JsonConverter
and add it to the serializer settings. Here's an example implementation of WriteJson
:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var Json = JObject.FromObject(value, serializer);
//modify Json by adding some properties ...
Json.WriteTo(writer);
}
However, this presents a few problems that I'm not sure how to get around:
If the serializer is configured by setting:
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
then attempting to call JObject.FromObject(value, serializer)
serializes nothing, since JSON.NET has already determined that the value is being serialized (and so it ignores further attempts at serializing the same reference).
If I set ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize
then I end up with an infinite recursion loop. Calling JObject.FromObject(value, serializer)
ends up re-entering WriteJson
of my custom JsonConverter
.
If instead I create a new serializer, or call JObject.FromObject(value)
(as shown in this example), I will no longer be using the same serializer settings. While the output would have the extra properties, it may not match the rest of the serialized json if other converters, contract resolvers, etc. have been configured for the original serializer. Additionally, I've lost the serialization context for reference loop handling, and so references that would have been skipped in the original output would no longer be skipped.
Is there any better way to hook into the serialization process that permits you to modify the json produced by the original serializer?
Update: For now I've implemented a less than desirable workaround which is to provide a settings factory Func<JsonSerializerSettings>
to my custom JsonConverter
. The provided settings have to exclude the converter to avoid the recursion loop and this doesn't preserve the serialization context. Additionally, any nested objects will not receive additional properties since the converter is no longer configured.
To ignore individual properties, use the [JsonIgnore] attribute.
JsonPropertyAttribute indicates that a property should be serialized when member serialization is set to opt-in. It includes non-public properties in serialization and deserialization. It can be used to customize type name, reference, null, and default value handling for the property value.
JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object). If you serialize this result it will generate a text with the structure and the record returned.
The JsonSerializer is a static class in the System. Text. Json namespace. It provides functionality for serializing objects to a JSON string and deserializing from a JSON string to objects.
JsonObjectAttribute(String) Initializes a new instance of the JsonObjectAttribute class with the specified container Id. JsonObjectAttribute(MemberSerialization) Initializes a new instance of the JsonObjectAttribute class with the specified member serialization.
I figured out that I can implement a custom IContractResolver
and override CreateProperties
to include any number of additional properties along with an IValueProvider
that provides the actual property values.
Instead of writing a JsonConverter
to serialize the object and then add properties, this approach modifies the contract for a particular type that is then used by the serializer.
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var Result = base.CreateProperties(type, memberSerialization);
//check if this is a type we add properties to
if (IsAnnotatedType(type))
{
Result.Add(new JsonProperty
{
PropertyType = typeof(string),
PropertyName = "AdditionalProperty",
Readable = true,
Writable = false,
//value provider will receive the object instance when GetValue is called
ValueProvider = new AdditionalPropertyValueProvider()
});
}
return Result;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With