Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a new property to JsonElement using System.Text.Json?

Let's say I have Dictionary<string, object> metas. The object could be JsonElement (System.Text.Json)

And I need iterate each element if the object is an array of elements, and add new property to each element.

I can iterate elements like this:

var metaValue = metas["key-1"];

if (metaValue is JsonElement elementValue && elementValue.ValueKind == JsonValueKind.Array)
 {
    // Iterate over the elements in the array
    foreach (var element in elementValue.EnumerateArray())
    {
        // add new property to element
        // like element["newproperty"] = "test value"
    }
 }

My question is, how to add new property to JsonElement?

like image 234
Youxu Avatar asked Sep 05 '25 21:09

Youxu


1 Answers

You cannot modify JsonElement, it is completely immutable. Instead, you must deserialize it to some mutable type such as JsonNode, modify the mutable type, then re-serialize back to JsonElement. The following method does that:

public static class JsonExtensions
{
    public static JsonNode TryAddPropertyToArrayElements<TProperty>(this JsonNode node, string name, TProperty value)
    {
        if (node is JsonArray array)
            foreach (var obj in array.OfType<JsonObject>())
                obj[name] = JsonSerializer.SerializeToNode(value);
        return node;
    }

    public static JsonElement TryAddPropertyToArrayElements<TProperty>(this JsonElement element, string name, TProperty value) =>
        element.ValueKind == JsonValueKind.Array
        ? JsonSerializer.SerializeToElement(JsonSerializer.Deserialize<JsonNode>(element)!.TryAddPropertyToArrayElements(name, value))
        : element;

    public static object? TryAddPropertyToArrayElements<TProperty>(this object? obj, string name, TProperty value) =>
        obj switch
        {
            JsonElement e => e.TryAddPropertyToArrayElements(name, value),
            JsonNode n => n.TryAddPropertyToArrayElements(name, value),
            null => null, // JSON values that are null are deserialized to the c# null value, not some element or node of type null
            _ => throw new ArgumentException("Unexpected type ${obj}"),
        };
}

Then you would use it as follows:

metas["key-1"] = metas["key-1"]?
    .TryAddPropertyToArrayElements("newproperty", "test value");

Demo fiddle #1 here.

Obviously all this serialization is somewhat inefficient. To avoid the extra serialization, assuming you generated your dictionary via deserialization, you could deserialize with JsonSerializerOptions.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode. This setting forces types declared as object to be deserialized to JsonNode rather than JsonElement:

var inputOptions = new JsonSerializerOptions
{
    UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode,
};
var metas = JsonSerializer.Deserialize<Dictionary<string, object?>>(json, inputOptions)!;

If you do you will be able to mutate its values directly using the method JsonExtensions.TryAddPropertyToArrayElements<TProperty>(this JsonNode node, string name, TProperty value) included above.

Notes:

  • JsonNode and JsonSerializer.SerializeToElement() were introduced in .NET 6 so if you are using an earlier version you will need to use a different approach.

Demo fiddle #2 here.

like image 92
dbc Avatar answered Sep 08 '25 22:09

dbc