System.Text.Json serializer issues

The system.text.json works fine when writing the document, i see all fields in the parent classes, but when loading from the database, only properties in the top most class get serialized.

Example:

Profile is base, EditableProfile is child.
I write EditableProfile to couchbase, all properties are there. When reading, i only see properties in EditableProfile get populated, nothing in Profile.

Am i missing something?

I don’t see System.text.json being used for the serializer in any of the examples. I believe the default serializer is JSON.NET

There’s a thread here Use System.Text.Json as DefaultSerializer?

Usually what helps for debugging is the code and the documents. And the EditableProfile and Profile classes.

It sounds like you may be using polymorphism in your document types. If so, you’ll need to use the JsonPolymorphic attribute or some other method of adding discriminators for deserialization.

https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism

i already have

[JsonPolymorphic]
[JsonDerivedType(typeof(EditableProfile))]

Can you provide a full example of the types and calls?

[JsonPolymorphic(
    UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor)]
[JsonDerivedType(typeof(EditableProfile))]
public class Profile
{

    [JsonPropertyName("profileGuid")]
    public string ProfileGuid;

    [JsonPropertyName("updatedAt")]
    public DateTime? UpdatedAt { get; set; }

    [JsonPropertyName("createdAt")]
    public DateTime? CreatedAt { get; set; }

    [JsonPropertyName("rvn")]
    public int Rvn { get; set; } = 0;
    
    [JsonPropertyName("accountId")]
    public string AccountId { get; set; } = "";

the document is:

{
  "accountIds": "Bhdsfdsfds",
  "updatedAt": "2024-04-16T23:41:24.0967139Z",
  "createdAt": "2024-04-16T23:41:24.0967682Z",
  "rvn": 0,
  "accountId": "sssssss",
  "type": "profile_main"
}

Editable profile has this prop:

public class EditableProfile : Profile
{
    [JsonPropertyName("accountIds")]
    public string AccountIds { get; set; } = "Bhdsfdsfds";

Only property to get serialized is accountIds.

    public async Task<EditableProfile> FindByKey(string accountId, string profileType, bool b)
    {
        var queryParameters = new Couchbase.Query.QueryOptions();
        queryParameters.Parameter("accountId", accountId);
        queryParameters.Parameter("profileType", profileType);
        string query;
        query = $@"SELECT * from profile WHERE accountId = $accountId AND type = $profileType LIMIT 1";
        var results = await _scopeService.QueryAsync<EditableProfile>(query, queryParameters);
        var Profile = await results.Rows.FirstOrDefaultAsync();
        return Profile;
    }
builder.Services.AddCouchbase(options =>
{
    builder.Configuration.GetSection("Couchbase").Bind(options);

    var jsonOptions = new JsonSerializerOptions
    {
        PropertyNameCaseInsensitive = true,
        IncludeFields = true,
        WriteIndented = true
    };
    
    options.WithSerializer(SystemTextJsonSerializer.Create(jsonOptions));

});

this is how i set the serializer

Try changing the projection in the query to profile.* instead of just *. I think you just have an issue with the query result rows being nested.

Sorry not sure i understand, new to couchbase

query = $@“SELECT profile.* from profile WHERE accountId = $accountId AND type = $profileType LIMIT 1”; like this?

Now its working, i am not sure why. Why does profile.* matter than just *?

Yes. Sorry, I’m responding from my phone so it’s hard to type everything out, especially code.

It has to do with the nature of SQL++ and structured data as compared to traditional SQL and flat data.

When you’re joining multiple extents such as this:

SELECT * FROM profile INNER JOIN user ON user.id = profile.userId

Then each extent is returned in the query rows array nested based on its name:

[
  {
    "profile": { "userId": 1, email: "user@gmail.com" },
    "user": { "id": 1, name: "User" }
  },
  {
    "profile": { "userId": 2, email: "user2@gmail.com" },
    "user": { "id": 2, name: "User2" }
  }
]

For consistency, this same rule applies to queries that only have a single extent, such as yours. So your original query output would be something like this:

[
  {
    "profile": {
      "accountIds": "Bhdsfdsfds",
      "updatedAt": "2024-04-16T23:41:24.0967139Z",
      "createdAt": "2024-04-16T23:41:24.0967682Z",
      "rvn": 0,
      "accountId": "sssssss",
      "type": "profile_main"
    }
  }
]

By using profile.* in the projection instead of * you’re requesting the attributes from the specific profile extent of the query, rather than requesting all extents, so it will return unnested:

[
  {
    "accountIds": "Bhdsfdsfds",
    "updatedAt": "2024-04-16T23:41:24.0967139Z",
    "createdAt": "2024-04-16T23:41:24.0967682Z",
    "rvn": 0,
    "accountId": "sssssss",
    "type": "profile_main"
  }
]
2 Likes