Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use null conditional operator in a dynamic JSON with JsonConvert.DeserializeObject

I'm using Newtonsoft to deserialize an known JSON object and retrieve some values from it if they're present.

The crux is there is that object structure may keep changing so I'm using dynamic to traverse the structure and retrieve the values. Since the object structure keeps changing, I'm using the null conditional operator to traverse the JSON.

The code looks like this

dynamic jsonMeta = JsonConvert.DeserializeObject<dynamic>(jsonScript);
string gVal = jsonMeta.a?.b?.c?.d?.e?.f?.g?.Value ?? ""

The whole idea of this is to traverse the object in a null safe manner so that if a member doesn't exist, it evaluates to null and it assigns it a default value without throwing an exception. But what I'm seeing is that I get a exception 'Newtonsoft.Json.Linq.JValue' does not contain a definition for 'e' if that member d is null.

My understanding is that while the Value of d is null, it's of type JValue so that's why the null conditional operator doesn't work, but then it tries to access member e inside d it throws the exception.

So my question is how can I make this work in C#? Is there an easy way to access the JSON members without knowing the JSON structure in a single line or relatively easy way?

like image 939
rboy Avatar asked Oct 22 '25 10:10

rboy


1 Answers

Another option is to use a dynamic ExpandoObject while deserializing the JSON with Newtonsoft.Json. This allows you to use the null conditional and null coalascing operators on the dynamic object.

dynamic jsonMeta = JsonConvert.DeserializeObject<ExpandoObject>(jsonScript, new ExpandoObjectConverter());
string gVal = jsonMeta.a?.b?.c?.d ?? ""

But this has a limitation that if it accesses an non-existent member the ExpandoObject will throw an exception (it is a sealed class and this behavior is hardcoded). So to work around this behavior I wrote a custom SafeExpandoObject class which wraps around the ExpandoObject and will allow you to access all/non-existent members without having an exception thrown, it will return null instead when you try to access a member that doesn't exist.

You can find the code here: https://codereview.stackexchange.com/questions/287243/wrapping-an-expandoobject-within-a-safeexpandoobject

Use it like this:

dynamic jsonMeta = JsonConvert.DeserializeObject<ExpandoObject>(jsonScript, new ExpandoObjectConverter());
jsonMeta = new SafeExpandoObject(jsonMeta); // Rewrap the ExpandoObject in a SafeExpandoObject that doesn't throw exceptions
string gVal = jsonMeta.a?.b?.c?.d ?? ""

Another point to note is that you cannot use direct indexing with ExpandoObject like jsonMeta["@name"].

To overcome this limitation you will need to either implement an IDictionary interface to the SafeExpandoObject to allow for direct indexing or cast the ExpandoObject it to a IDictionary<string, object> and then index it like

dynamic jsonMeta = JsonConvert.DeserializeObject<ExpandoObject>(jsonScript, new ExpandoObjectConverter());
string a = (jsonMeta as IDictionary<string, object>)["@name"];
like image 58
rboy Avatar answered Oct 25 '25 01:10

rboy