mbraceproject / FsPickler

A fast multi-format message serializer for .NET
http://mbraceproject.github.io/FsPickler/
MIT License
326 stars 52 forks source link

json rpc 2.0 #20

Closed StefanBelo closed 8 years ago

StefanBelo commented 10 years ago

Json rpc 2 packs returned data into result parameter, for instance:

{"jsonrpc": "2.0", "result": 19, "id": 3}

Any error is reported back in error parameter:

{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}

We could say that result and error are optional parameter because if request is successful the error parameter is not required, and if error is returned no result parameter is returned.

I tested FsPicker json deseralization on the following data:

type ErrorData =
    {
        code: int
        message: string
    }

type JsonRpcResponse<'T> =
    {
        result: 'T
        error: ErrorData
    }

type AccountDetailsResponse = 
    {
        currencyCode: string
        firstName: string
        lastName: string
        localeCode: string
        region: string
        timezone: string
        discountRate: double
        pointsBalance: int
    }

let runTest() = 
    let json = """{"jsonrpc":"2.0","result":{"currencyCode":"EUR","firstName":"Stefan","lastName":"Belopotocan","localeCode":"en","region":"GBR","timezone":"CET","discountRate":0.0,"pointsBalance":0},"id":1}"""
    let json2 = """{"result":{"currencyCode":"EUR","firstName":"Stefan","lastName":"Belopotocan","localeCode":"en","region":"GBR","timezone":"CET","discountRate":0.0,"pointsBalance":0},"error":{"code":-32700,"message":"DSC-0008"}}"""

    let jsp = FsPickler.CreateJson(indent = true, omitHeader = true)

    let data = jsp.UnPickleOfString<JsonRpcResponse<AccountDetailsResponse>>(json2)

    printfn "%A" data

For “json” data this code throws exeception:

{"expected property 'result' but was 'jsonrpc'."}

Because json data starts with the parameter: jsonrpc, what is just syntactical sugar for json rpc 2 response message. If I remove parts which are not used by my mapping model: JsonRpcResponse and use sample defined by json2 value, the data are deserialized correctly, but that is not the way how real json api responses are used in my application, as the same api method could returned response without some optional parameters.

What was my first naïve approach was to declare error parameter as optional but it does not work. Even if you change order of properties, for instance in the record AccountDetailsResponse you declare currencyCode as third parameter and not as the first one as it is returned by api, then deserialization fails. In real api applications you do not need to map all api models to your domain models as well.

It would be nice if your library would map only selected properties skipping not used ones.

eiriktsarpalis commented 10 years ago

My opinion is you should be using Json.NET for this. FsPickler.Json is intended at providing a faithful and performant representation of arbitrary .NET objects to Json. In that sense, trying to derive .NET objects from a protocol specification rather than the other way around is unnatural and perhaps impossible.

A possible fix for the deserialization error that you are getting would be to add a jsonrpc : string property at the start of your record.

Adding support for optional fields and random orderings is possible, but adds to the complexity and comes with a certain performance penalty. I find it unlikely to have this added in the near future.

StefanBelo commented 8 years ago

Actually not json.net, but NetJSON if you did not hear about it yet. Definitely better performance with NetJSON.