Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Json.NET Serialization with Extra Properties; Preserving Serializer Settings

Tags:

c#

json.net

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.

like image 784
Michael Petito Avatar asked Dec 02 '14 17:12

Michael Petito


People also ask

Which of the following attributes are required to ignore properties for JSON serializer?

To ignore individual properties, use the [JsonIgnore] attribute.

How does the Jsonproperty attribute affect JSON serialization?

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.

What is the point of JSON serialization?

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.

Which one is correct class for JSON serializer?

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.

What is JsonObjectAttribute?

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.


1 Answers

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;
}
like image 89
Michael Petito Avatar answered Sep 24 '22 16:09

Michael Petito