WebApi odata: Serialize long as string
Asked Answered
C

3

6

I'm migrating form WCF Data Service to Web API odata v4. WCF Data Service did serilize long values in quotes:

{
   "value":[{
     "ID":"4527895973896126465"
   },{
     "ID":"4527895973896126466"
   }]
}

Web API odata does not:

{
   "value":[{
     "ID":4527895973896126465
   },{
     "ID":4527895973896126466
   }]
}

This means that I loose the precision of the 64bit number during JSON.parse in JavaScript, since JavaScript numbers are only 53bit.

Does WebApi has a build in mechanism to handle long values as string values? I'm thinking of the IEEE754Compatible header element. But this has no effect on the generated response. Am I overlooking something?

An alternative solution would be to deserilaize 64bit numbers as string values during JSON.parse on the client side. Is this possible?

Conquistador answered 7/5, 2015 at 12:52 Comment(0)
C
3

Finally I got this to work. OdataLib does indeed support this by the IEEE754Compatible parameter. It check's the response Content-Type header to see if the parameter is present.

The thing is, that the header value doesn't automatically get propageted to the response header by the web api framework. You have to do it on your own. I've build an ODataController derived class that patches the IEEE754Compatible parameter into the Content-Type header of the response like so:

public abstract class ODataControllerIEEE754Compatible : ODataController 
{
    private void PatchResponse(HttpResponseMessage responseMessage)
    {
        if (responseMessage != null && responseMessage.Content != null)
        {
           if (this.Request.Content.Headers.GetValues("Content-Type").Any(
               h => h.Contains("IEEE754Compatible=true")))
           {
               responseMessage.Content.Headers.TryAddWithoutValidation(
                  "Content-Type", "IEEE754Compatible=true");
           }
       }
    }

    public override Task<HttpResponseMessage> ExecuteAsync(
       HttpControllerContext controllerContext, CancellationToken cancellationToken)
    {
            var response = base.ExecuteAsync(
               controllerContext, cancellationToken);
            response.Wait(cancellationToken);

            PatchResponse(response.Result);

            return response;
    }
}

Now by sending the IEEE754Compatible=true parameter in the Content-Type Header I receive all long values serialized as JSON strings:

GET http://localhost/some/url HTTP/1.1
OData-Version: 4.0;
Content-Type: application/json;odata.metadata=minimal;IEEE754Compatible=true;charset=utf-8
Cache-Control: no-cache

HTTP/1.1 200 OK
Content-Type: application/json;odata.metadata=minimal;IEEE754Compatible=true
Server: Microsoft-HTTPAPI/2.0
OData-Version: 4.0

{
  "@odata.context":"http://localhost/some/url","value":[
{
  "ID":"4527895973896126465", ...
Conquistador answered 29/5, 2015 at 13:16 Comment(0)
S
0

While I don't know much about ASP.net, I can give you a rexeg which can be used to add quotation marks around big numbers in a JSON. Here I set it to any number of 16 digits or more.

http://jsfiddle.net/yryk70qz/1/
value.replace(/:\s*(\d{16,})(\s*[,\}])/g, ':"$1"$2');

You could do it with all numbers regardless of their length with:
value.replace(/:\s*(\d+)(\s*[,\}])/g, ':"$1"$2');

(inpired by this question: Convert all the integer value to string in JSON)

Salicylate answered 7/5, 2015 at 16:23 Comment(1)
Thanks for this. I could use this as a "last resort", but I wonder if this (or something similar) is possible during JSON.parse (for better performance). Best would be to get the old behaviour I had with wcf data services on the server side.Conquistador
S
0

@Jeldrik's answer works, but here's a much cleaner way to do the same.

public class IEEE754CompatibleAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        var parameter = actionExecutedContext.Request.Headers.Accept
            .SelectMany(h => h.Parameters.Where(p =>
                p.Name.Equals("IEEE754Compatible", StringComparison.OrdinalIgnoreCase) &&
                p.Value.Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase)))
            .FirstOrDefault();

        if (parameter != null)
        {
            actionExecutedContext.Response.Content?.Headers.ContentType.Parameters.Add(parameter);
        }
    }
}

Put this [IEEE754Compatible] attribute on any OData controller you want to start honoring IEEE754Compatible=true. Alternatively, add new IEEE754CompatibleAttribute() to the GlobalFilterCollection to make it work for every controller automatically.

With this in place, requests that specify something like Accept: application/json; IEEE754Compatible=true should give you a response that has it's long number values converted into strings.

Shushubert answered 13/11, 2019 at 20:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.