I would like to accept an object of a known type where one of the properties is simply a Record<string, any>. Currently the only way I can get this to work is to force my client to JSON.stringify the property and then I can deserialize it just fine. The issue is that that’s not necessary with any other framework I’ve ever used, so I’d like to allow the client to just pass JSON.
Here is what I’d like to pass, pretending we have events that we POST about specific objects in a system:
{
"eventType" : "created",
"objectId" : "cb01e673-3835-44a8-a803-7cbfd437f0e2",
"eventDetails" :
{
"anyKey" : "someValue",
"someArray" : ["no problem!", "no reason this should not work!"],
"someNestedObject" :
{
"anyNestedKey" : "should never be an issue"
}
}
}
Here is my request object:
public class ObjectEvent
{
public string? ObjectId { get; set; }
public string? EventType { get; set; }
public dynamic? EventDetails { get; set; }
}
Problem is, when I get EventDetails it looks like this:
Request ValueKind = Object : "{ "whatever": "go for it" }"
This means it’s now a JsonElement, but that’s not true. It was just a Json object passed from the client. I can SEE the object there but I cannot seem to get it into a dynamic.
Again, if I force everyone to serialize the "EventDetails" property as a string before POSTing, I can switch "dynamic? EventDetails" to "string? EventDetails" and use Newtonsoft.Deserialize and it works fine, but that’s not really acceptable when people are using curl and postman to access this or writing jest tests. They should be able to pass an object and .NET should accept it, which it kind of is but not in a way that I can use.
Any suggestions? Thank you in advance.
2
Answers
I figured it out, much simpler than it might appear!
Given that I have a RequestModel and then a Model that I'm trying to marshal the data to for business logic:
Then the adapter looks like this:
First off, I’d be questioning the design of an API where "anything goes" is a good idea. Usually there are some limitations that can/should be in place (e.g. level of nesting).
Other point: using
dynamic
is in no way different than usingobject
– except the point at which any errors are thrown compile time forobject
vs runtime fordynamic
. And even if dynamic would be giving some benefits, you have no way of knowing at design time what will be inside (if you did, you could use that knowledge to shape the class definition instead of usingdynamic/object
).Anyway as to a proposed solution: check the docs for
System.Text.Json
for handling case of overflow JSON – i.e. anything that is not explicitly defined. (If you are usingNewtonsoft.Json
then either migrate (based on which version of .net are you using) or try finding the equivalent way for that library.In the end you will need to work with the
JsonElement
class anyway. As you write yourself:The bold text is true, the text in italics is not: it is
JsonElement
and you have it in thedynamic
and code like this will work just fine:or just change the class definition: