Is it possible to deserialize json string into dynamic object using System.Text.Json?
Asked Answered
L

3

31

I am using System.Text.Json package to use the serialization and deserialization.

I can deserialize a json string into an object when the type is explicitly specified like below.

var data = JsonSerializer.Deserialize<PersonType>(jsonString);

But the dynamic type does not work. Is it possible to deserialize without having to specify the type? Thank you!

var data = JsonSerializer.Deserialize<dynamic>(jsonString);
Lector answered 31/3, 2022 at 15:30 Comment(10)
Does this answer your question? Deserialize JSON into C# dynamic object?Schooling
@Schooling OP is specifically asking about System.Text.Json. Although using another deserialiser would work, I don't think it's relevant to this question.Goyette
github.com/dotnet/runtime/issues/31175Alkyd
@Goyette unless System.Text.Json is an abslute necessity to use, it offers a couple of nice and easy ways to accomplish that task. Granted it does not slove the issue with using System.Text.Json, I'm not even sure it can do that (to be fair I rarely use it anyway)Schooling
Seems like you could use JsonNode in .Net 6 - github.com/dotnet/runtime/issues/31175#issuecomment-937646022Goyette
It's not built in to System.Text.Json, but you could use a custom converter. ObjectAsPrimitiveConverter from this answer to C# - Deserializing nested json to nested Dictionary<string, object> has an option to deserialize JSON objects to .NET ExpandoObjects. Demo here: dotnetfiddle.net/1Y6hI6. Is that what you want?Chesterton
You can refer to this answer with an example of parsing JSON with dynamic using System.Text.Json: https://mcmap.net/q/471335/-taking-a-json-array-with-file-paths-to-update-the-json-array-for-date-last-modifiedHeyward
@RahulSharma your answer in that question refers to Newtonsoft.JSON, doesn’t it? The issue I linked to above says it is not - and not planned to be - possible to deserialise to dynamic with System.Text.Json because it would mean adding a dependency - see github.com/dotnet/runtime/issues/31175#issuecomment-937646022Alkyd
@Alkyd Please go through the answer. The second portion of the answer uses the MS library for parsing JSONHeyward
I did read the answer - it uses JsonDocument.Parse which is a good way of doing it IMO - but I’m not seeing an actual dynamic object?Alkyd
S
43

tl:dr JsonNode is the recommended way but dynamic typing with deserializing to ExpandoObject works and I am not sure why.

It is not possible to deserialize to dynamic in the way you want to. JsonSerializer.Deserialize<T>() casts the result of parsing to T. Casting something to dynamic is similar to casting to object

Type dynamic behaves like type object in most circumstances. In particular, any non-null expression can be converted to the dynamic type. The dynamic type differs from object in that operations that contain expressions of type dynamic are not resolved or type checked by the compiler. The compiler packages together information about the operation, and that information is later used to evaluate the operation at run time

docs.

The following code snippet shows this happening with your example.

var jsonString = "{\"foo\": \"bar\"}";
dynamic data = JsonSerializer.Deserialize<dynamic>(jsonString);
Console.WriteLine(data.GetType());

Outputs: System.Text.Json.JsonElement

The recommended approach is to use the new JsonNode which has easy methods for getting values. It works like this:

JsonNode data2 = JsonSerializer.Deserialize<JsonNode>(jsonString);
Console.WriteLine(data2["foo"].GetValue<string>());

And finally trying out this worked for me and gives you want you want but I am struggling to find documentation on why it works because according to this issue it should not be supported but this works for me. My System.Text.Json package is version 4.7.2

dynamic data = JsonSerializer.Deserialize<ExpandoObject>(jsonString);
Console.WriteLine(data.GetType());
Console.WriteLine(data.foo);
Struve answered 31/3, 2022 at 16:54 Comment(6)
Regarding your observation that JsonSerializer.Deserialize<ExpandoObject>() mysteriously works, my guess is that System.Text.Json is able to deserialize to ExpandoObject because it has a parameterless constructor and implements IDictionary<string, object>. However, the values are getting deserialized as JsonElement rather than as .NET primitives or nested expandos, which you can see if you do Console.WriteLine($"data.foo={data.foo}, data.foo.GetType()={data.foo.GetType()}");. Demo here: dotnetfiddle.net/nxKBB2. So there's no deserialization to a dynamic hierarchy built in.Chesterton
@Chesterton do you know what is the solution to convert it to primitive types recursively ?Mount
@SrivathsaHarishVenkataramana - It's not built in to System.Text.Json, but you could use a custom converter. ObjectAsPrimitiveConverter from this answer to C# - Deserializing nested json to nested Dictionary<string, object> has an option to recursively deserialize JSON objects to .NET ExpandoObjects and primitives. Demo here: dotnetfiddle.net/1Y6hI6Chesterton
@Chesterton you're a System.Text.json hero! ThanksMount
It's good that this solution exists but I'm pretty surprised it's not built into System.Text.Json - I can't imagine why anyone would want a dynamic object with properties that are raw strings and numbers are actually of type JsonElement.Litharge
The ExpandoObject only works because Console.WriteLine() implicitily calls .ToString(). It won't work if you're using a non-string value as there will be no auto-conversion. If you try to assign the value to a string variable it also won't work unless you explicitly call .ToString() or use .GetValue<T>() to cast to the appropriate type. It's good that we can do this but disappointing they didn't provide a better dynamic interface to walk these values in the same way that NewtonSoft's JsonValue/JsonObject does.Duisburg
L
3

I have tried using System.Text.Json in a dynamic way and it just does not work in an easy and meaningful way it seems. So while not a direct answer to your question, but I was "forced" to use the good old Newtonsoft.Json that just works:

dynamic result = JObject.Parse(message);
Laccolith answered 20/6, 2022 at 8:3 Comment(0)
P
-1

Yes, you can do the above JGilmore's answer and get a JsonNode or... JsonObject. But, specifically, you can also get a dynamic if you so desire by specifying object as well:

var jsonString = "{\"foo\": \"bar\"}";
dynamic dynam1 = JsonSerializer.Deserialize<object>(jsonString);
var jsonString2 = Convert.ToString(dynam1);  //Or ToString or Serialize...

To work with a JsonObject instead of a JsonNode, you should get System.Text.Json.Nodes, then:

JsonNode data2 = JsonSerializer.Deserialize<JsonNode>(jsonString);
var jsonObject = data2.AsObject();
var subNode = data2["foo"]  //yay, you got the value as a node... woot...

var objVal = subNode.GetValue<object>(); //wonky hope if you want int, it isn't string.
string objStr = objVal?.ToString();
Parmentier answered 2/1 at 23:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.