lucas lucas - 3 months ago 15
C# Question

JSON response does not include all items

For some reason only the first item of each array is being returned as JSON, any clues why?

Here is what I see during debugging, as you can tell, I have two items in 'Category' and two items in 'Tasks':

enter image description here

Postman JSON result (it should return all items, shouldn't it?):
enter image description here

For reference, here is my 'Category.cs':

public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public DateTime Timestamp { get; set; }
public string Username { get; set; }

public ApplicationUser ApplicationUser { get; set; }

public virtual ICollection<Task> Tasks { get; set; }
}


My 'Task.cs':

public class Task
{
public int TaskId { get; set; }
public string Name { get; set; }
public DateTime Timestamp { get; set; }

public virtual Category Category { get; set; }
}


and my Api:

[HttpGet]
public JsonResult Get()
{
var result = _repo.GetAllForUser("lucas@test.com");

return Json(result);
}


And repository:

public IEnumerable<Category> GetAllForUser(string name)
{
return _ctx.Categories
.Where(c => c.ApplicationUser.UserName == name)
.Include(c => c.Tasks)
.ToList();
}


Here is what I insert into database, and what I should retrieve from the Api:

categories.Add(new Category
{
Name = "cat 1",
Tasks = new List<Task>
{
new Task { Name="task 1" },
new Task { Name="task 2" }
}
});
categories.Add(new Category
{
Name = "cat 2",
Tasks = new List<Task>
{
new Task { Name="task 3" },
new Task { Name="task 4" }
}
});

Answer

As Kiran pointed out, you have circular references in your models, which is causing an exception. This bug is incorrectly making it look like the request is completing with partial data. (The circular reference is Category -> Tasks -> Task -> Category)

What's actually happening is an unhandled exception halfway through the JSON serialization of the response. Instead of aborting the connection (as it should), ASP.NET Core is sending back everything that was serialized until the error occurred.

You can either define a DTO class that doesn't include the reference from Task back to Category, or return an anonymous type:

[HttpGet]
public JsonResult Get()
{
    var result = _repo.GetAllForUser("lucas@test.com");

    var response = new {
        categoryId: result.CategoryId,
        name: result.Name,
        timestamp: result.Timestamp,
        username: result.Username,
        tasks: result.Tasks.Select(t => new {
            taskId: t.TaskId,
            name: t.Name,
            timestamp: t.Timestamp
        })
    };

    return Json(response);
}

If you do this often, it makes sense to create DTO class and use a tool like AutoMapper to do the mapping for you.

Comments