contentful.net icon indicating copy to clipboard operation
contentful.net copied to clipboard

[v7.1.0] Serialization Error when using a reference in an Entity

Open annogram opened this issue 2 years ago • 1 comments

Hello everyone. the tl;dr of this issue is that I have an Entity A which contains a reference to an Entiy B as part of its payload. When I call GetEntries<B>() for all the entries it's working fine. However, when I call GetEntries<A>() (which contains a reference to entry A) it fails with the following:

 System.ArgumentException: The value "Project { ProjectName = , Summary = , Description = , RelatedSkills = , Videos = , Documents = , Sys = Contentful.Core.Models.SystemProperties }" is not of type "Models.Contentful.Skill" and cannot be used in this generic collection. (Parameter 'value')

Background

I have these record types:

Skill

public record Skill(string SkillName, string EquipmentType, Asset? SkillIcon, Asset? SkillEquipment, SystemProperties Sys);

Project

public record Project(
    string ProjectName, 
    string Summary, 
    string Description,
    List<Skill> RelatedSkills, <=== failing to deserialise this
    List<Asset> Videos,
    List<Asset> Documents,
    SystemProperties Sys);

I have a very simple controller for a backend delivery api which simply uses the SDK function, and a conversion to send to a UI

    [HttpGet]
    [Route("projects")]
    public async Task<ActionResult<IEnumerable<ProjectDto>>> GetProjects()
    {
        var projectItems = await _client.GetEntries<Project>();
        var projects = ProjectDto.ConvertProjects(projectItems);
        return Ok(projects);
    }

The exception is thrown on the GetEntries<Project>() call as you can see below:

image

I'd like to take full advantage of the SDK to do this rather than use the sys link and manually generate nested entities.

Any help here would be appreciated

Full stack trace

System.ArgumentException: The value "Project { ProjectName = , Summary = , Description = , RelatedSkills = , Videos = , Documents = , Sys = Contentful.Core.Models.SystemProperties }" is not of type "Im[REDACTED]BFF.Models.Contentful.Skill" and cannot be used in this generic collection. (Parameter 'value')
   at System.Collections.Generic.List`1.System.Collections.IList.Add(Object item)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
   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.ResolvePropertyAndCreatorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObjectUsingCreatorWithParameters(JsonReader reader, JsonObjectContract contract, JsonProperty containerProperty, ObjectConstructor`1 creator, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator)
   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.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
   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.Linq.JToken.ToObject(Type objectType, JsonSerializer jsonSerializer)
   at Newtonsoft.Json.Linq.JToken.ToObject[T](JsonSerializer jsonSerializer)
   at Contentful.Core.ContentfulClient.GetEntries[T](String etag, String queryString, CancellationToken cancellationToken)
   at Contentful.Core.ContentfulClient.GetEntries[T](String queryString, CancellationToken cancellationToken)
   at Im[REDACTED]BFF.Controllers.ContentfulController.GetProjects() in D:\Repository\im-[REDACTED]-fyi\Im[REDACTED]BFF\Controllers\ContentfulController.cs:line 30
   at lambda_method6(Closure , Object )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS
=======
Accept: text/plain
Host: localhost:7229
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36
:method: GET
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: _rails_session=7gnmVyPSWqSkJ57ERzNg5GdOqSGKF8MosynnXY9jm335QeuZqVcq2pVXGmTusvO1ukBdXn380%2BVeTXq2FdpGyvp12rH%2FxPzXDQhZHCBdfH%2FJtdI8DbCLv59MuQQGeoC1H%2BinJzbuec1iJizdwJMOakJyzGTAtuP2xC%2BZTJygHThF%2FmT6R55i4mmAfZblI%2FBkJYAJJwUgMxG%2F1Ha0wx3v6FklzZri36wzsOo3Cn7LRfMwt5p4ffZ7MtoS7HXzfCEDrG90Sky%2BYlRepNOBXbHSJ9IX%2Fte%2BTHAQMqKR3BlVaX0DSxYsg1cEVWCkldWhUKkZhEQSiZ%2FfszHVOe3MXHsgbGcQFUvaX7%2FEH6C46U5EbOYJlET13DllFb2sF964%2Bv%2FiAzgeTV1n%2Bu%2FTVkpxpkGP5P2%2B9uzyL%2BBpRoCc1ACkLfysBQhMrIFIVtcmoOGnRhPNcI2eDo%2BWcf3Cjoi%2FTov8gwLl4x%2BLE0TvpE%2Fs1KRNFMQajm2r%2BDxyObPR4kpAa2oTBlnAJ%2BzRllw8U5h6t6RvekJypj67gEKZ1CBv9qc%2B%2BNuZ5%2BRpMkvv9%2F3FrSTDIl872ukf7TpIjAlKfay1yAXXwPV8zfPbDDcEFiV972F2AphGUyWh1OQCKKWNHhDBm6Hrh2Fh88dKA7ZOmrh%2FhN69hp6lcP7n1ImGqCfm1KdW8z%2FoQSGIHfldFeMUwb2rbMbYJX3zUFHsNixx8HuJ8TEET1hGsIPfQdgHSkhjJfm1wnzY8oqHa3wgvhlhoLSSvmkQRTtZOJbnUeNdoQ%3D%3D--LfYO%2FjlfOsI%2BErrN--d%2BTdFcYola1LdPOOQHbsNQ%3D%3D
Referer: https://localhost:7229/swagger/index.html
sec-gpc: 1
sec-fetch-site: same-origin
sec-fetch-mode: cors
sec-fetch-dest: empty

annogram avatar Aug 31 '22 10:08 annogram

Hi @annogram It sounds like you're trying to put an already deserialized item of a specific type into a collection that only contains items of a different type.

It's hard to say exactly what's going on, but are you perhaps using Skill/Project interchangeably for the same content type?

If so have a look at: https://www.contentful.com/developers/docs/net/tutorials/using-net-cda-sdk/#get-entries-of-multiple-types-or-by-interface

Roblinde avatar Aug 31 '22 11:08 Roblinde

Hey @Roblinde thanks for the quick response!

That's what the error is saying but that's not the case what you see here is basically the entire error path. There is no inheritance or interfaces on these records they simply declare the fields and the Project Entity references the skill entity. it fails right out of the GetEntities<T>() call.

There is no circular referencing or anything either it's simply those contents I might take a look at using the query builder and see if there's any success.

Skill

image

Project

image

annogram avatar Aug 31 '22 19:08 annogram

FYI I've fixed this by explicitly using a query builder instead of relying on the parameterless version of GetEntries.

Old

[HttpGet]
[Route("projects")]
public async Task<ActionResult<IEnumerable<ProjectDto>>> GetProjects()
{
    var projectItems = await _client.GetEntries<Project>();
    var projects = ProjectDto.ConvertProjects(projectItems);
    return Ok(projects);
}

New

[HttpGet]
[Route("projects")]
public async Task<ActionResult<IEnumerable<ProjectDto>>> GetProjects()
{
    var query = QueryBuilder<Project>.New.ContentTypeIs("project").Include(2);
    var projectItems = await _client.GetEntries(query);
    var projects = ProjectDto.ConvertProjects(projectItems);
    return Ok(projects);
}

annogram avatar Sep 01 '22 10:09 annogram