JsonSerializer.Deserialize is returning empty object
Asked Answered
W

2

9

I have a webapi that returns some Json:

{"id":9,"businessName":"dummy","address":"pluto","products":[
{"id":762,"description":"Centralized needs-based website","price":1281.24,"stock":1600,"categories":[],"factory":null},
{"id":1027,"description":"Realigned 6th generation knowledge base","price":2398.16,"stock":19583,"categories":[],"factory":null},
{"id":1392,"description":"User-centric zero administration array","price":998.07,"stock":6124,"categories":[],"factory":null},
{"id":1800,"description":"Team-oriented reciprocal core","price":4422.95,"stock":17372,"categories":[],"factory":null},
{"id":2763,"description":"Sharable needs-based hierarchy","price":4122.98,"stock":17397,"categories":[],"factory":null},
{"id":6189,"description":"Re-engineered hybrid emulation","price":395.09,"stock":532,"categories":[],"factory":null}
]}

I then try to deserialize using:

using var response = await _httpClient.GetAsync($"{GetApiRouteFromEntity(entity)}/{entity.GetId()}");
        response.EnsureSuccessStatusCode();
        var responseContent = await response.Content.ReadAsStringAsync();
        T? item = JsonSerializer.Deserialize<T>(responseContent);

but this gives me an empty Factory with id 0 and all the other attributes to null

Factory.cs

public class Factory : EntityBase
    {
        [DisplayName("ID")]
        public int Id { get; set; }

        [DisplayName("Nome Business")]
        public string? BusinessName { get; set; }

        [DisplayName("Indirizzo")]
        public string? Address { get; set; }

        [DisplayName("Prodotti")] 
        public virtual ICollection<Product> Products { get; set; }
        
        public override string ToString()
        {
            return $"[{Id}] {BusinessName}";
        }
    }

Product.cs

public class Product : EntityBase
    {
        [DisplayName("ID")]
        public int Id { get; set; }

        [DisplayName("Descrizione")]
        public string? Description { get; set; }

        [DisplayName("Prezzo")]
        public float Price { get; set; }

        [DisplayName("Magazzino")]
        public int Stock { get; set; }

        [DisplayName("Categorie")]
        public virtual ICollection<Category> Categories { get; set; }

        [DisplayName("Fabbrica")]
        public virtual Factory Factory { get; set; }

        public override string ToString()
        {
            return $"[{Id}] {Description}";
        }
    }

EntityBase.cs

public abstract class EntityBase
    {
        public virtual object GetId()
        {
            return GetType().GetProperty("Id").GetValue(this);
        }
    }

i guess its because the factory prop in the products is null but idk how to work around that

Whipcord answered 17/2, 2022 at 12:24 Comment(5)
responseContent is full with the Json at the end of the request, but the serializer just retunrs an empty entityWhipcord
So T in JsonSerializer.Deserialize<T>(responseContent) is Factory ?Totter
yes, in fact when debugging item is a Factory but it has every prop as default valueWhipcord
Your JSON is invalid. There is no : between "products" and its arrayUnpeopled
i just pasted the json incorrectly, ty for making me notice!Whipcord
T
8

From the docs:

By default, property names and dictionary keys are unchanged in the JSON output, including case.

You can specify the property naming policy:

T? item = JsonSerializer.Deserialize<T>(responseContent, new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});

to use camel case for all JSON property names, or mark all needed properties with JsonPropertyNameAttibute containing correct name:

public class Factory : EntityBase
{
    [DisplayName("ID")]
    [JsonPropertyName("id")] // and so on
    public int Id { get; set; }
    ....
}

Or set JsonSerializerOptions.PropertyNameCaseInsensitive to true.

Totter answered 17/2, 2022 at 12:33 Comment(9)
oh my god, i spent 5 hoiurs into this, ty so much!Whipcord
@IvanAmbla was glad to help!Totter
i have a new problem now :-:, if i try to serialize the object it gives me a nice string with PascalCase but if i try and use JsonSerializer.Serialize(lastEntity.Entity,new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }) it just gives me {}Whipcord
@IvanAmbla are you using the same settings when serializing and deserialiazing? Or the json is coming from another system?Totter
same settings, im just putting instead of gettingWhipcord
for now i found a workaround using Newtonsoft JsonConvert.SerializeObject but it would be better using the native jsonserializerWhipcord
Can you please post full repro somewhere? Cause if you are using the same settings as in answer it should not return pascal case. There is another option - make property names case insensitive - new JsonSerializerOptions { PropertyNameCaseInsensitive = true };Totter
using the same options returns {} using no options return Pascal, ill prepare a repo if necessary i will take me some time, ty for the help again!Whipcord
i can open a new discussion, i think its better since even if related is a different problem ill open it this eveningWhipcord
Z
13

The JSON keys are all camel case but the props in your classes are written in pascal case. E.g. businessName vs. BusinessName. Hence JsonSerializer can't match them.

You can initialize the serializer like this to ignore the differences in casing:

var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};
T? item = JsonSerializer.Deserialize<T>(responseContent, options);

See the docs for further info: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-character-casing

Zoan answered 17/2, 2022 at 12:35 Comment(0)
T
8

From the docs:

By default, property names and dictionary keys are unchanged in the JSON output, including case.

You can specify the property naming policy:

T? item = JsonSerializer.Deserialize<T>(responseContent, new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});

to use camel case for all JSON property names, or mark all needed properties with JsonPropertyNameAttibute containing correct name:

public class Factory : EntityBase
{
    [DisplayName("ID")]
    [JsonPropertyName("id")] // and so on
    public int Id { get; set; }
    ....
}

Or set JsonSerializerOptions.PropertyNameCaseInsensitive to true.

Totter answered 17/2, 2022 at 12:33 Comment(9)
oh my god, i spent 5 hoiurs into this, ty so much!Whipcord
@IvanAmbla was glad to help!Totter
i have a new problem now :-:, if i try to serialize the object it gives me a nice string with PascalCase but if i try and use JsonSerializer.Serialize(lastEntity.Entity,new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }) it just gives me {}Whipcord
@IvanAmbla are you using the same settings when serializing and deserialiazing? Or the json is coming from another system?Totter
same settings, im just putting instead of gettingWhipcord
for now i found a workaround using Newtonsoft JsonConvert.SerializeObject but it would be better using the native jsonserializerWhipcord
Can you please post full repro somewhere? Cause if you are using the same settings as in answer it should not return pascal case. There is another option - make property names case insensitive - new JsonSerializerOptions { PropertyNameCaseInsensitive = true };Totter
using the same options returns {} using no options return Pascal, ill prepare a repo if necessary i will take me some time, ty for the help again!Whipcord
i can open a new discussion, i think its better since even if related is a different problem ill open it this eveningWhipcord

© 2022 - 2024 — McMap. All rights reserved.