Reputation: 13161
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.
Upvotes: 5
Views: 2470
Reputation: 13161
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;
}
Upvotes: 9