Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to deserialize array via GET

I am using Kendo UI's DataSource to send sorting information to my ServiceStack service. I know that this has been addressed if you're using using POST, but I want to adhere to RESTful designs, so the request verb needs to be GET. I'm using this snippet to test with:

var dataSource = new kendo.data.DataSource({
        serverSorting: true,
        sort: [{ field: "ProductName", dir: "desc" },
               { field: "Category", dir: "asc"}],

        serverPaging: true,
        page: 2,
        pageSize: 5,

        transport: {

            read: {
                url: "/products",
                dataType: "json",
                contentType: "application/json",
                dataType: "jsonp"
            },

            parameterMap: function (data, type) {
                //return kendo.stringify(data);
                //return JSON.stringify(data);
                //return $.param(data, true);
                //data.sort = kendo.stringify(data.sort);
                return data;
            }
        }
    });

    dataSource.fetch(function () {
        console.log(dataSource.view());
    });

The sorting parameters get turned into a jagged array like:

sort[0][field]: ProductName
sort[0][dir]: desc
sort[1][field]: Category
sort[1][dir]: asc

My request DTO is:

    public class SortTerm
{
    public string field { get; set; }
    public string dir { get; set; }
}

public class KendoQuery
{

    public List<SortTerm> Sort { get; set; }

    public int Skip { get; set; }
    public int Take { get; set; }
    public int Page { get; set; }
    public int PageSize { get; set; }
}

All of the simple parameters get deserialized, but how on earth can I transform the Sort property, either client-side or server-side, to have it populate correctly?

Notice that I've tried a variety of serialization techniques in the parameterMap function and I'm completely stumped.

EDIT

So this all boils down to: How do I pass an array of objects via $.get() to a ServiceStack service, when jQuery thoughtfully rearranges my request into a jagged array? This stinks of a request filter, but I have to imagine its been solved before for **GET** requests.

like image 925
jklemmack Avatar asked Feb 21 '26 15:02

jklemmack


2 Answers

You cannot use complex data structures, such as that of the jagged sorting array, with a GET request using standard techniques because JSON GET requests only support simple variables as these can be translated to request parameters in the query string. So the request will form correctly for the simple parameters ?Page=2&PageSize=5 ... but for sort[0][field] this cannot be specified as a query string request parameter.

However you could work around this if you where to stringify your search criteria object using datasource.transport.parameterMap so that it can be passed as a parameter, which is then converted at the server.

parameterMap: function (data, type) {
    // Convert the search criteria to a JSON string and store it on value sortJson
    data.sortJson = JSON.stringify(data.sort);

    // Remove the sort value, as this will be provided by sortJson
    delete data.sort;

    // Return the data
    return data
}

At the server you will need to handle converting the JSON string to the List<SortTerm>:

public class KendoQuery
{

    List<SortTerm> sort;
    public List<SortTerm> Sort {
         get {
             // Handles deserialising the SortJson value 
             if(sort == null)
                 sort = ServiceStack.JsonSerializer.DeserializeFromString<List<SortTerm>>(SortJson);
             return sort;
         }
    }

    public string SortJson { get; set; } 
    public int Skip { get; set; }
    public int Take { get; set; }
    public int Page { get; set; }
    public int PageSize { get; set; }
}

I hope this helps.

like image 129
Scott Avatar answered Feb 24 '26 05:02

Scott


Scott's answer above is the right one, but here is my more server-centric solution. Basically, I manually reconstruct the SortTerm object from the query string. This is extendable to the other Kendo-specific parameters, like Filters & Groups.

Client-code:

<html>
<head>
    <meta charset="utf-8">
    <title>Kendo UI Snippet</title>

    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="http://cdn.kendostatic.com/2014.1.528/js/kendo.all.min.js"></script>
</head>
<body>
    <script>
        var dataSource = new kendo.data.DataSource({
            serverSorting: true,
            sort: [{ field: "ProductName", dir: "desc" },
                   { field: "Category", dir: "asc" }],

            serverPaging: true,
            page: 2,
            pageSize: 5,

            transport: {
                read: {
                    url: "/products",
                    dataType: "json"
                }
            }
        });

        dataSource.fetch(function () {
            console.log(dataSource.view());
        });
    </script>
</body>
</html>

Server code (in AppHost implementation):

this.GlobalRequestFilters.Add((req, resp, dto) =>
{
    if (dto is KendoQueryBase)
    {
        KendoQueryBase qb = dto as KendoQueryBase;
        if (qb.Sort == null) qb.Sort = new List<SortTerm>();
        Dictionary<string, string> qs = req.QueryString.ToDictionary();
        var i = 0;
        while (qs.ContainsKey("sort[{0}][field]".Fmt(i)))
        {
            qb.Sort.Add(new SortTerm()
            {
                field = qs["sort[{0}][field]".Fmt(i)],
                dir = qs["sort[{0}][dir]".Fmt(i)]
            });
            i++;
        }
    }
});
like image 33
jklemmack Avatar answered Feb 24 '26 04:02

jklemmack



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!