-1

I have the following code:

var model = new
{
    Name = "Alexander"
};

var serializedModel = JsonSerializer.Serialize(model);
var deserializedModel = JsonSerializer.Deserialize<object>(serializedModel);
var name = deserializedModel!.GetType().GetProperty("Name");

The name variable is null as the "Name" property doesn't seem to exist. I do not have the model object when deserializing but just the JSON string.

I have tried using JsonConvert.DeserializeObject(serializedModel) from Newtonsoft and that seems to be working fine. However, I want to use System.Text.Json. Also, I have to use .GetType().GetProperty(...) as it's done like that in the external library I'm passing the deserialized object to.

user113000
  • 17
  • 7
  • If `.GetType().GetProperty()` must work and you cannot define the class at compile time in any way, you can't use `System.Text.Json` as that simply will not generate types at runtime, or provide `dynamic` implementations that do so like Newtonsoft does. While you can write all of that yourself anew as a custom converter, it seems like a terrible waste of effort to do that if Newtonsoft already provides it. If the JSON can be fixed at compile time, generating non-anonymous types from it to deserialize it is simple and can be done in a variety of ways (paste as classes, code generators). – Jeroen Mostert Mar 28 '23 at 22:47
  • Does this answer your question? [Getting nested properties with System.Text.Json](https://stackoverflow.com/questions/61553962/getting-nested-properties-with-system-text-json) – Martin Mar 28 '23 at 23:04
  • Using Newtonsoft just to deserialize it might look a bit stupid but it seems to be most straightforward way as I do have to use `.GetType().GetProperty` and I don't know the type of the original model. @mason Well it kind does depend as I don't know the type at all and the library uses .GetProperty to get all properties and their values. – user113000 Mar 28 '23 at 23:12
  • @JeroenMostert It seems the class being deserialized to doesn't necessarily have to be known at compile time. But that doesn't mean that you can't take the name value or any other properties obtained from the JSON and assign them to an instance of a class, and then pass that instance to the external library. – mason Mar 28 '23 at 23:12
  • @mason Yes I guess using JsonDocument and JsonElement to get the properties and their values, and then create an object instance with said properties, is going to work. But that seems like a really inefficient way to do that. Since System.Text.Json doesn't seem to support it out of the box, I guess I will have to either do that or use Newtonsoft. – user113000 Mar 28 '23 at 23:16
  • @mason: Did you actually mean to address that comment to me, or to the OP? As I don't see how it relates to my comment. If `.GetType().GetProperty()` must work (because code that can't be modified uses that), then there must be a type providing the property, and that type must either exist at compile time, or be generated at runtime, or use something like `DynamicObject`/`IDynamicMetaObjectProvider`, no way around that. It *can* be an anonymous type, but there's no particular advantage to that. If the requirement that `.GetType().GetProperty()` works is dismissed, other solutions are possible. – Jeroen Mostert Mar 28 '23 at 23:18
  • @mason: Ah, I see. I assumed the consumer side would also be doing something dynamic in terms of getting the properties, but if we know what properties it gets in advance (i.e. that side is *not* dynamic) then yes, we can just use any old type that provides the required properties, regardless of how we construct it. This seems improbable, though, in that the consuming side would be more likely to just declare an appropriate type than fondle around dynamically for properties if they're perfectly static. – Jeroen Mostert Mar 28 '23 at 23:24
  • Did you see [Deserialize anonymous type with System.Text.Json](https://stackoverflow.com/q/59313256/3744182)? That approach may be easier than using `JsonElement`. Or if an `ExpandoObject` would work for you, you could use `ObjectAsPrimitiveConverter` from [this answer](https://stackoverflow.com/a/65974452/3744182) to [C# - Deserializing nested json to nested Dictionary](https://stackoverflow.com/q/65972825/3744182). – dbc Mar 29 '23 at 06:50
  • Also, your existing code doesn't work with Newtonsoft. `JsonConvert.DeserializeObject(serializedModel)!.GetType().GetProperty("Name")` returns `null`, see https://dotnetfiddle.net/pIMoeP. So honestly it's hard to see what the requirements of your question are, since Newtonsoft currently doesn't behave in the way you need. Please [edit] to clarify what help you need, ideally with a [mcve] showing Json.NET code that does work and System.Text.Json code that does not. See also [ask]. – dbc Mar 29 '23 at 20:30

4 Answers4

1

JsonSerializer.Deserialize<object>(serializedModel) is equivalent to var JsonSerializer.Deserialize<JsonElement>(serializedModel); so depending on your requirements you can use it:

var deserializedModel = JsonSerializer.Deserialize<JsonElement>(serializedModel);
if (deserializedModel.TryGetProperty("Name", out var nameP))
{
    Console.WriteLine(nameP.ToString());
}

For experimentation purposes you can use non-generic method and provide Type instance (doubtful that it will be useful in real app):

var deserializedModel = JsonSerializer.Deserialize(serializedModel, model.GetType());

But usually it is better to stick to standard approach and just create class to use for deserialization. I.e.

class MyClass
{
    public string Name { get; set; }
}

var x = JsonSerializer.Deserialize<MyClass>(serializedModel);

Also you can look in other ways to perform dynamic deserialization via System.Text.Json (including JsonNode and JsonDocument APIs)

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • I have to use `.GetType().GetProperty(...)` as I'm just passing the deserialized object to an external library and that uses said methods to get the property. Also, as said in my answer, I do not have the model, I just have the JSON string. I put the model just for an example. And the type is anonymous object, I don't have classes as I don't know what kind of JSON I will get. – user113000 Mar 28 '23 at 23:07
  • 1
    @user113000 then I'm afraid that you will need to use Newtonsoft then. Also I wonder how exactly your code looks, cause I was not able to make it work with Newtonsoft. I.e. `var deserializeObject = JsonConvert.DeserializeObject(serializedModel); var name1 = deserializeObject!.GetType().GetProperty("Name");` returns null for me. – Guru Stron Mar 28 '23 at 23:14
  • Hmm thanks for noticing that. Seems like the library internally calls `.IsAnonymous()` on the passed object and uses `.GetType().GetProperty()` only if that returns true. If you use Newtonsoft then that returns JObject and `.IsAnonymous()` is false. In that case the library works correctly. I will have investigate their code in order to figure out how that works. – user113000 Mar 28 '23 at 23:32
0

you need only one line of code

string name = (string) JsonObject.Parse(serializedModel).AsObject()["Name"]; //"Alexander"

UPDATE

But if you want it the ridiculous way , IMHO this it is

var deserializedModel = JsonSerializer.Deserialize<object>(serializedModel);

string name = ((JsonElement)deserializedModel).GetProperty("Name").GetString();

I am sorry but I will never understand why it is working this way. When I am working with text.json I just try to do everything the most akward way, inside out, and it is usully helps. You have to ask a text.json team how they are doing this kind of libraries.

just to compare using newtonsoft and text.json

//using Newtonsoft.Json;
string name = (string) JObject.FromObject(model)["Name"];

//using System.Text.Json;
var serializedModel = JsonSerializer.Serialize(model);
string name = JsonSerializer.Deserialize<JsonElement>(serializedModel)
                                      .GetProperty("Name").GetString();

Serge
  • 40,935
  • 4
  • 18
  • 45
  • The method invoked here is `JsonNode.Parse`; while calling it through `JsonObject` is legal, it seems unnecessarily confusing to do so (for starters, it's harder to find that way). Doing so of course also doesn't change the return type (that's still `JsonNode?`), which explains why the `.AsObject()` is necessary. – Jeroen Mostert Mar 28 '23 at 23:05
  • I have to use `.GetType().GetProperty(...)` as I'm just passing the deserialized object to an external library and that uses said methods to get the property. – user113000 Mar 28 '23 at 23:06
0

You may not know the "original" type that led to the JSON. But if you know something about the shape (such as "it always has a string property called Name") there's nothing stopping you from deserializing to a type that matches that shape. For example:

using System;
using System.Text.Json;
                    
public class Program
{
    public static void Main()
    {
        var model = new
        {
            Name = "Alexander"
        };
        
        var serializedModel = JsonSerializer.Serialize(model);
        
        var deserializedModel = JsonSerializer.Deserialize<HasName>(serializedModel);
        SomeExternalLib.Foo(deserializedModel);         
    }
}

class SomeExternalLib
{
    public static void Foo(object o)
    {
        var nameProperty = o.GetType().GetProperty("Name");
        var name = nameProperty.GetValue(o);
        Console.WriteLine("I got the name: " + name);
    }
}

class HasName
{
    public string Name { get; set; }
}

Runnable in this fiddle.

mason
  • 31,774
  • 10
  • 77
  • 121
  • Thanks a lot for the effort but I don't know the shape either. I will try creating a converter and see how that goes. – user113000 Mar 28 '23 at 23:35
  • @user113000 What do you mean you don't know the shape? If you don't know the shape of the object, how is the code supposed to find the value you're seeking? What other shapes could it come in? – mason Mar 28 '23 at 23:37
  • It's a templating engine so it uses the view code (which you pass as a string) to figure out which properties to look for. However, I was able to get it working by using `var deserializedModel = JsonSerializer.Deserialize(serializedModel, typeof(ExpandoObject));` as it turns out to be using `.GetType().GetProperty(...)` only if `object.IsAnonymous()` is true. Also, I was able to write a converter using `System.Reflection.Emit` but that kind seems too complex to be posted as an answer. Tomorrow morning I will see if I can simplify that. Thanks for the effort! – user113000 Mar 29 '23 at 00:37
  • 1
    @user113000 Then you should have posted the details of the exact templating engine, or been clearer about the limitations. What you've just said is not the same as what you originally asked, and those who answered you attempted to answer the question you actually asked. – mason Mar 29 '23 at 00:39
0

After spending some time on the original issue I was able to get it running using Reflection.Emit and that might actually be the only way to achieve what I originally asked for as you have the properties from the JSON and you have to generate a class at runtime that has said properties.

A more detailed answer can be found here: https://stackoverflow.com/a/29428640

If you don't actually need something so complex then you can try to deserialize to ExpandoObject.

user113000
  • 17
  • 7