Click here to Skip to main content
15,892,839 members
Articles / Web Development / HTML
Technical Blog

Serializing A PagedList Using JSON.NET In ASP.NET MVC – Gotcha And Workaround

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
30 Oct 2014CPOL3 min read 15.3K   4   1
Serializing a PagedList using JSON.NET in ASP.NET MVC – Gotcha And Workaround

Introduction

Recently, I discovered that there’s no one standard way for AJAX-driven server-side paging in ASP.NET MVC (in Web API, you can expose an IQueryable). For the case in hand, I decided to use PagedList for the server bit of the game.

PagedList

The PagedList interface looks a bit like this (for demonstration only, real code is a bit different, check its source code for the real stuff):

JavaScript
<script src="https://gist.github.com/Meligy/c2b7afebf45384519a20.js?file=PagedList.cs">

<noscript> 
<div class="line" id="file-pagedlist-cs-LC1">  
    public interface PagedList<T> : IEnumerable<T>  {
      int Count { get; }
      int PageCount { get; set; }
      int TotalItemCount { get; set; }
      int PageNumber { get; set; }
      int PageSize { get; set; }
      bool HasPreviousPage { get; set; }
      bool HasNextPage { get; set; }
      bool IsFirstPage { get; set; }
      bool IsLastPage { get; set; }
      int FirstItemOnPage { get; set; }
      int LastItemOnPage { get; set; }
      IPagedList GetMetaData();
      T this[int index] { get; }
  }

</noscript>

</script>

It provides nice properties for paging, and exposes itself as enumerable and has an indexer. Apart from this snippet, the library also provides an extension method ToPagedList() to apply to any enumerable and allow it to populate the properties from it (by enumerating on it, and by calling the Count() method).

We were also using JSON.NET for JavaScript serialization, which is pretty much defacto standard nowadays.

The JSON.NET Serialization Problem

JSON.NET has a nice implementation, if you serialize a class that implements IEnumerable<T>, and you don’t have a special treatment rule for it (via what JSON.NET calls “Converter” classes), when you serialize an instance of the class to JavaScript, it will be serialized as a JavaScript array, where the enumerated objects are the contents of this array. Makes lots of sense right?

Well, yes, except when you have a custom collection like PagedList, and you want to treat it as an object that has several properties, not as an array. JSON.NET does provide a solution for this actually, all you need to do is apply the [JsonObject] attribute to your class.

Unless you don’t own the source code of this class.

In this case, you need to inherit from it. By doing this, I lose the nice ToPagedList() extension method (because it creates an object of the PagedList class directly), but luckily it does nothing but calling new PagedList() with the parameters we give it, so, we don’t lose much.

Here’s how my implementation looks like:

JavaScript
<script src="https://gist.github.com/Meligy/c2b7afebf45384519a20.js?file=SerializablePagedList.cs">

<noscript> 
<div class="line" id="file-serializablepagedlist-cs-LC1"
>public class SerializablePagedList<T> : PagedList<T>{
    public SerializablePagedList(IQueryable<T> superset, int pageNumber, int pageSize)
        : base(superset, pageNumber, pageSize)
    {
    }  
    
    public SerializablePagedList(IEnumerable<T> superset, int pageNumber, int pageSize)
        : base(superset, pageNumber, pageSize)
    {
    }  
    
    public IEnumerable<T> Items
    {
        get { return base.Subset; }
    } 
}

<noscript>

</script>

Apart from having to copy the constructors to pass parameters to the base ones, have you noticed the extra Items property in there?

That’s because the Subset member it includes is actually a field, not a property, and JSON.NET won’t serialize that by default, I could override it somewhere else, but since I’m fiddling with this here, it made sense to just stick a property to the class.

Bonus: A Bit More On Implementation

In my actual code, I have added the Dynamic Linq NuGet package, and assumed a single property for sorting (which was fair for the situations where I intend to use this), so, I complemented the code above with another class that looks like this:

JavaScript
<script src="https://gist.github.com/Meligy/c2b7afebf45384519a20.js?file=ListPagingOptions.cs">

<noscript> 
<div class="line" id="file-listpagingoptions-cs-LC1">  public class ListPagingOptions  {
      public string SortPropertyName { get; set; }
      public bool SortDescending { get; set; }
      public int PageSize { get; set; }
      public int PageNumber { get; set; }
 
      public ListPagingOptions()
      {
          PageNumber = 1;
      }
 
      private IQueryable<T> ApplySortingToQuery<T>(IQueryable<T> query)
      {
          var sortExpression = SortPropertyName;
 
          if (SortDescending)
          {
              sortExpression += " descending";
          }
 
          return query.OrderBy(sortExpression);
      }
 
      public PagedList<T> CreatePagedListFromQueryable<T>(IQueryable<T> query)
      {
          query = ApplySortingToQuery(query);
 
          return new SerializablePagedList<T>(query, PageNumber, PageSize);
      }
 
      [JsonObject]
      public class SerializablePagedList<T> : PagedList<T>
      {
          public SerializablePagedList(IQueryable<T> superset, int pageNumber, int pageSize)
              : base(superset, pageNumber, pageSize)
          {
          }
 
          public SerializablePagedList(IEnumerable<T> superset, int pageNumber, int pageSize)
              : base(superset, pageNumber, pageSize)
          {
          }
 
          public IEnumerable<T> Items
          {
              get { return base.Subset; }
          }
      }
  }

</noscript>

</script>

This allows the controller to instantiate an instance of the SerializablePagedList class, pass it all the way to whatever repository method I have.

The repository method will take it as a parameter, work out what IQueryable it needs, and instead of passing it to UI, it calls CreatePagedListFromQueryable(), which returns an innocent-looking PagedList object (because SerializablePagedList inherits PagedList) that the repository can pass back to the controller, which can serialize it to JavaScript without a problem, then the rest is all JavaScript code to work out how create the paging parameters, and how to use the returned paging hints.

Even more, now that I think about it, maybe I should change the return type to SerializablePagedList, to make the Items property visible to the developer (because they’d think it’s magic, and in coding, magic is BAD). I’ll leave this as an exercise for you :)

Final Words / Disclaimer

The motivation behind this post is that I found the problem of serializing PagedList using JSON.NET a challenge and I wanted to help others work it out faster than I did. Is this how I’d recommend doing paging? Well, I don’t know, but if it’s what you choose, I hope that I have saved you some time.

And more importantly, is it good enough to be the defacto standard I mentioned I was after in the beginning of the post? Not really. I think it’s not bad, but definitely not the best. I’d love to see less clever (read: hacky), and more simpler solutions.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Senior Consultant at Readify
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionDemo Pin
Member 1102682631-Oct-14 17:44
Member 1102682631-Oct-14 17:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.