N
Novu•16mo ago
Jelle

.NET Client JSON Serializer exception when deserializing CreateSubscriber response

Hey guys, I'm trying out the .NET sdk (v0.2.2) pointing to api.novu.co (the default) and when creating a Subscriber I get the following exception:
Refit.ApiException: An error occured deserializing the response.
---> Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Novu.DTO.AdditionalDataDto]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'data._organizationId', line 1, position 27.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize[T](JsonReader reader)
at Refit.NewtonsoftJsonContentSerializer.FromHttpContentAsync[T](HttpContent content, CancellationToken cancellationToken) in /_/Refit.Newtonsoft.Json/NewtonsoftJsonContentSerializer.cs:line 56
at Refit.RequestBuilderImplementation.DeserializeContentAsync[T](HttpResponseMessage resp, HttpContent content, CancellationToken cancellationToken)
at Refit.RequestBuilderImplementation.<>c__DisplayClass14_0`2.<<BuildCancellableTaskFuncForMethod>b__0>d.MoveNext()
Refit.ApiException: An error occured deserializing the response.
---> Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Novu.DTO.AdditionalDataDto]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'data._organizationId', line 1, position 27.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize[T](JsonReader reader)
at Refit.NewtonsoftJsonContentSerializer.FromHttpContentAsync[T](HttpContent content, CancellationToken cancellationToken) in /_/Refit.Newtonsoft.Json/NewtonsoftJsonContentSerializer.cs:line 56
at Refit.RequestBuilderImplementation.DeserializeContentAsync[T](HttpResponseMessage resp, HttpContent content, CancellationToken cancellationToken)
at Refit.RequestBuilderImplementation.<>c__DisplayClass14_0`2.<<BuildCancellableTaskFuncForMethod>b__0>d.MoveNext()
The subscriber does end up getting created though, and when I run the request manually there's really nothing odd about the response (see attached)
No description
20 Replies
Pawan Jain
Pawan Jain•16mo ago
@unicodeveloper @benlin1994 👀
Jelle
JelleOP•16mo ago
I'll try to repro this in a clean project later
Novu_Bot
Novu_Bot•16mo ago
@vinoyl, you just advanced to level 1!
Prosper
Prosper•16mo ago
@dr.really please can you help check what's happening here?
dr.really
dr.really•16mo ago
Checking
Jelle
JelleOP•16mo ago
So this happens when doing GetSubscriber, UpdateSubscriber and CreateSubscriber but GetSubscribers does work. Looks to me like the response from those 3 is deserialized directly as a SubscriberDto which has the following property:
[JsonProperty("data")]
public List<AdditionalDataDto>? Data { get; set; }
[JsonProperty("data")]
public List<AdditionalDataDto>? Data { get; set; }
The Get/Update/Create subscriber methods deserialize the following response into a SubscriberDto:
{
"data": {
"_organizationId": "..",
"_environmentId": "..",
"firstName": "..",
"lastName": "..",
"phone": "..",
"subscriberId": "44429e69-f529-4e36-a1e5-21bd22c9958d",
"email": "..",
"avatar": "..",
"channels": [],
"_id": "..",
"deleted": false,
"createdAt": "2023-08-19T06:28:25.041Z",
"updatedAt": "2023-08-19T06:28:25.041Z",
"__v": 0,
"id": "64e0610948249e7e7683896d"
}
}
{
"data": {
"_organizationId": "..",
"_environmentId": "..",
"firstName": "..",
"lastName": "..",
"phone": "..",
"subscriberId": "44429e69-f529-4e36-a1e5-21bd22c9958d",
"email": "..",
"avatar": "..",
"channels": [],
"_id": "..",
"deleted": false,
"createdAt": "2023-08-19T06:28:25.041Z",
"updatedAt": "2023-08-19T06:28:25.041Z",
"__v": 0,
"id": "64e0610948249e7e7683896d"
}
}
So it looks to me that it's attempting to serialize the whole response as a List<AdditionalDataDto> Doing this gives me direct access to the SubscriberDto
var subscriber = await _novu.Subscriber.GetSubscriber(user.Id);
var email = subscriber.Email; // example access
var subscriber = await _novu.Subscriber.GetSubscriber(user.Id);
var email = subscriber.Email; // example access
While it should basically be similar to the result of the GetSubscribers method
var email = subscriber.Data.Email;
var email = subscriber.Data.Email;
public class SubscribersDto
{
[JsonProperty("page")]
public int Page { get; set; }

[JsonProperty("totalCount")]
public int TotalCount { get; set; }

[JsonProperty("pageSize")]
public int PageSize { get; set; }

[JsonProperty("data")]
public SubscriberDto[] Data { get; set; }
}
public class SubscribersDto
{
[JsonProperty("page")]
public int Page { get; set; }

[JsonProperty("totalCount")]
public int TotalCount { get; set; }

[JsonProperty("pageSize")]
public int PageSize { get; set; }

[JsonProperty("data")]
public SubscriberDto[] Data { get; set; }
}
Prosper
Prosper•16mo ago
@dr.really any updates?
todd
todd•16mo ago
@vinoyl If you are interested there is a pull request pending for the client library. If you look in the Tests project that are some Integration and Acceptance tests that may help. see https://github.com/novuhq/novu-dotnet/pull/46
Jelle
JelleOP•16mo ago
I can make a fix for it, thanks So basically it's just adding this that's the easiest, similar to TriggerResponseDto, so the ISubscriberClient methods return SubscriberResponseDto instead of directly SubscriberDto
public class SubscriberResponseDto
{
[JsonProperty("data")] public SubscriberDto? Data { get; set; }
}
public class SubscriberResponseDto
{
[JsonProperty("data")] public SubscriberDto? Data { get; set; }
}
It does break the existing usages though, not sure it really matters since the existing code doesn't work?
Prosper
Prosper•16mo ago
@vinoyl have you made the fix for it?
Jelle
JelleOP•16mo ago
@unicodeveloper Well I do have the fix ready, based on the PR branch that @todd7201 mentioned but was waiting to see if it's okay to make a breaking change or if i need to make a workaround like a custom json converter so it doesnt break any usages? And if I can add it to the existing PR or if I need to make a PR from my own fork? In the meantime I've just been continuing the integration with the pr branch + my change, let me know how you'd like me to proceed 🙂
Novu_Bot
Novu_Bot•16mo ago
@vinoyl, you just advanced to level 2!
Prosper
Prosper•16mo ago
@dr.really will really be the best to advise how to proceed here. He's the still taking a look at @todd7201's large PR
todd
todd•16mo ago
I have added a comment https://github.com/novuhq/novu-dotnet/pull/46#discussion_r1300529902 that I can't reproduce the error against 0.17.1 which is the latest deployment docker script. @vinoyl the problem arose with 0.18.0 release and is now fixed in the pull request.
Jelle
JelleOP•16mo ago
That's great! Sorry I wasn't very involved any more, been a busy few days
dr.really
dr.really•16mo ago
GitHub
Release v0.3.0 · novuhq/novu-dotnet
Breaking Change This release implements breaking changes that will require refactoring. Please take a moment and check out the readme for updates. Thanks to @toddb for his massive work on this rele...
dr.really
dr.really•16mo ago
I would also like to welcome a new co-contributor to the repo, @todd7201
Pawan Jain
Pawan Jain•16mo ago
Thanks a lot @dr.really and @todd7201 🤩
Prosper
Prosper•16mo ago
Welcome @todd7201 thank you for this release. 🔥 🚀
Want results from more Discord servers?
Add your server