Description
We're experimenting with how we can provide the most intuitive, developer friendly experience for reading and writing additional properties for OpenAPI and are struggling to use [JsonExtensionData]
.
The ideal experience for end users would be
- As intuitive as creating an anonymous object or
Dictionary<string, object?>
- Symmetrical, in both the signature of the property, but ideally also the concrete types
- The output is consistent whether the JSON number is short or big.
As it is today, there are three signatures to use with [JsonExtensionData]
:
IDictionary<string, JsonElement>
:- Positive: This is great for reading properties and forcing the end user to very explicitly and verbosely pick the data type. For example, from a JSON number, you can't really predict whether a property will consistently return an int, double, decimal, float, etc, and the
JsonElement
forces the user to explicitly make that decision, feJsonElement.GetUInt16()
. - Negative: To set the value you have to create a JsonElement which isn't intuitively created and I assume has poor performance characteristics, fe:
recordForReading.AdditionalProperties["author"] = JsonDocument.Parse("\"J.R.R. Tolkien\"").RootElement
- Positive: This is great for reading properties and forcing the end user to very explicitly and verbosely pick the data type. For example, from a JSON number, you can't really predict whether a property will consistently return an int, double, decimal, float, etc, and the
IDictionary<string, object?>
:- Positive: This is as intuitive as it can possibly be in .NET to write additional properties without needing to create JsonNode's or JsonElement's. Just put in data into the value and the JSON serializer takes care of properly writing it.
- Negative:
- When you're creating the additional properties, and immediately read them, the values have the original types you gave them, but when it is serialized and deserialized, the values are
JsonElement
's. Maybe this isn't actually a problem, but I'm worried this will surprise our users. - When deserializing JSON, the user sees
object
but the concrete type is alwaysJsonElement
which seems unintuive/deceiving. I would personally naively check foris string
oris int
etc.
- When you're creating the additional properties, and immediately read them, the values have the original types you gave them, but when it is serialized and deserialized, the values are
JsonObject
:- Positive:
- It's almost as intiutive as
Dictionary<string, object?>
because simple values are implicitly cast toJsonNode
's, fe:recordSymmetrical.AdditionalProperties["author"] = "J.R.R. Tolkien"
. - The property is consistent and predictable a
JsonObject
.
- It's almost as intiutive as
- Negative:
- Some types can't be implicitly casted, fe:
string[] tags = ["fantasy", "adventure"]; recordSymmetrical.AdditionalProperties["tags"] = JsonValue.Create(tags);
JsonObject
withExtensionData
cannot be used to write values currently, because of an open bug.
- Some types can't be implicitly casted, fe:
- Positive:
IMO, the best experience for reading is IDictionary<string, JsonElement>
and JsonObject
, while the best writing experience is IDictionary<string, object?>
, and the best of both worlds is JsonObject
if the bug were to be resolved.
additionalProperties
in OpenAPI, the equivalent of ExtensionData
is becoming more common needed with us gaining traction in the AI and embedding space (we work with Pinecone on their .NET SDK).
FYI, I wrote a little sample that plays with these different signatures here, and they have indexers to make it more intuitive.
For context, we generate code based on multiple spec formats (OpenAPI/AsyncAPI/proto) and merge them into intuitive SDKs, and I am personally invested in giving .NET devs the best SDKs possible balancing developer experience and performance. How would you recommend providing intuitive classes with extension data that can be used for both writing and reading?