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?
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.
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