How can I access properties of dynamic object deserialized from a string using System.Text.Json?

Viewed 1221
using System.Text.Json;
using System.Text.Json.Serialization;

public static dynamic StringToObject(string str) {
    dynamic JsonObjectFromString = JsonSerializer.Deserialize<dynamic>(str);
    System.Console.WriteLine(JsonObjectFromString) // this line show correct json object with a correct type
    System.Console.WriteLine(JsonObjectFromString["cookies"]) // this line will error
    return JsonObjectFromString;
}

Basically when I try to access any property of dynamically deserialized object, my program errors. Is there any way to use JsonSerializer.Deserialize dynamically?

The string is basically from httpbin.

{
  "cookies": {}
}

Btw this doesn't happen with NewtonSoft.Json.

Error:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best overloaded method match for 'System.Text.Json.JsonElement.this[int]' has some invalid arguments
         at CallSite.Target(Closure , CallSite , Object , String )
         at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
         at myNamespace.myClass.StringToObject() in D:\lesha\screener\src\StringToObject.cs:line 12
         at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
1 Answers

Typically, when trying to reference specific elements of a json string without deserializing into a strongly typed object, you do not want to fully deserialize the object since you only use a subset of the data. In these cases, use the JsonDocument.Parse API:

var json = "{\"cookies\": { \"id\": 1 } }";

var options = new JsonDocumentOptions { AllowTrailingCommas = true };

using var document = JsonDocument.Parse(json, options);
Console.WriteLine(document.RootElement.GetProperty("cookies"));

If you absolutely want to use dynamic, there is no way to retrieve part of the structure since the underlying type, JsonElement, does not contain a Runtime binder that allows you to walk the object. As a result, you have two options: you can either cast the dynamic to JsonElement, or you can directly deserialize to JsonElement. This allows you to use the GetProperty method.

var str = "{\"cookies\": { \"id\": 1 } }";
var JsonObjectFromString = JsonSerializer.Deserialize<JsonElement>(str);
Console.WriteLine(JsonObjectFromString);
Console.WriteLine(((JsonElement)JsonObjectFromString).GetProperty("cookies"));
Related