codecutout / JsonApiSerializer

JsonApiSerializer supports configurationless serializing and deserializing objects into the json:api format (http://jsonapi.org).
MIT License
113 stars 46 forks source link

De-serialization issue when attribute is an array of objects #118

Open AndreCarvalho opened 4 years ago

AndreCarvalho commented 4 years ago

Hi, given the following contracts

   public class ResourceA
    {
      public string Id { get; set; }
      public string Type { get; set; } = "A";
      public ResourceB ResourceB { get; set; }
    }

    public class ResourceB
    {
      public string Id { get; set; }
      public string Type { get; set; } = "B";
      public ResourceC[] ResourceCs { get; set; }
    }

    public class ResourceC
    {
      public string Id { get; set; }
      public string Type { get; set; } = "C";
      public DateTimeOffset DateTimeOffset { get; set; }
    }

and the following jsonapi instance

{
  "data": {
    "id": "8a45fc9f-8d08-49d1-9f21-43fe7d27b47f",
    "type": "A",
    "relationships": {
      "resourceB": {
        "data": {
          "id": "20fa444b-38d7-481f-ac00-ef86b92fc8c0",
          "type": "B"
        }
      }
    }
  },
  "included": [
    {
      "id": "20fa444b-38d7-481f-ac00-ef86b92fc8c0",
      "type": "B",
      "attributes": {
        "resourceCs": {
          "data": []
        }
      }
    }
  ]
}

what we get with v1.7.4 is an instance of ResourceA with a property of ResourceB with an array containing a default instance of ResourceC, with Id=null, and DateTimeOffset=default(DateTimeOffset) (instead of an empty array). With version 1.3.1, the array was empty as expected. I noticed that renaming attributes to relationships does work but in our case it comes from an external service..

In any case, is this a bug? Thanks

alex-davies commented 4 years ago

ResourceCs is in the attributes so its treated as regular JSON serialization, it does not do any special handling of data property, its expecting your ResourceC object to have a data property to deserialize into. It is the equivalent of JsonConvert.DeserializeObject<ResourceC>(@"{""Data"":[]}"), which will return a default ResourceC (Id=null, and DateTimeoOffset=defalt(DateTimeOffset))

The model that more accurately fits your JSON is

public class ResourceA
{
    public string Id { get; set; }
    public string Type { get; set; } = "A";
    public ResourceB ResourceB { get; set; }
}

public class ResourceB
{
    public string Id { get; set; }
    public string Type { get; set; } = "B";
    public ResourceCData ResourceCs { get; set; }
}

public class ResourceCData
{
    public ResourceC[] Data { get; set; }
}

public class ResourceC
{
    public string Id { get; set; }
    public string Type { get; set; } = "C";
    public DateTimeOffset DateTimeOffset { get; set; }
}

ResourceC is not real Resource object, it is just an attribute that happens to have similar fields to a resource object.