core icon indicating copy to clipboard operation
core copied to clipboard

No ability to define custom rules of json serialization

Open fpawel opened this issue 9 years ago • 11 comments

And its hard to undestand how websharper decodes/encodes json data. For example These data

{ "user": "sdf",  "pass": "zxczxc"  }

are recognized correctly to object of this type:

type UserPass = { user: string; pass : string }

but if we change the order of the properties in the json object like this:

{ "pass": "zxczxc", "user": "sdf"  }

we get just NoEncodingException

fpawel avatar Mar 18 '16 10:03 fpawel

This definitely looks like a bug, it should work regardless of the field order.

Tarmil avatar Mar 18 '16 10:03 Tarmil

I can't seem to reproduce the issue, the following works correctly here:

    type EndPoint =
        | [<EndPoint "POST /"; Json "userpass">] Home of userpass: UserPass

    and UserPass = { user: string; pass : string }

    [<Website>]
    let Main =
        Application.MultiPage (fun ctx (EndPoint.Home userpass) ->
            Content.Text(sprintf "%s : %s" userpass.user userpass.pass)
        )

Tarmil avatar Mar 18 '16 11:03 Tarmil

My code is more complicated because I can not use the syntax of named fields in the discrimanated unions on the target platform (appharbor in my case, older version of F#)

module Api =     
    type UserPass = { 
        user: string; 
        pass : string }
    type Login = {
        id : int
        [<Json>] data : UserPass }
    type EndPoint =
        | [<EndPoint "POST /login">] Login of Login ...

    let dispatch context = function 
        | Login { data = userpass } -> ...

type EndPoint =
    | [<EndPoint "/api">] Api of Api.EndPoint  ..

Application.MultiPage (fun ctx -> function
    | Api api -> Api.dispatch ctx api
   ... )

fpawel avatar Mar 18 '16 12:03 fpawel

Is there an acceptable way to get a textual body content of the http request?

fpawel avatar Mar 18 '16 12:03 fpawel

And it works fine if I use record type with just one field

type UserPass = { 
        user: string; 
        ///pass : string 
}

fpawel avatar Mar 18 '16 12:03 fpawel

This is quite strange, because using your code above and querying POST /api/login/123 with { "pass": "zxczxc", "user": "sdf" } as the body works here. I don't see what difference could make it fail on your side. Are you using the latest WebSharper?


To get the whole request body you can do:

use tr = new System.IO.StreamReader(ctx.Request.Body)
let input = tr.ReadToEnd()

Of course, since it's inside the handler, it will only be run if the request was matched.


Btw, I think you should be able to use a more recent version of F# on AppHarbor by grabbing the compiler from NuGet instead of using the preinstalled one.

Tarmil avatar Mar 18 '16 12:03 Tarmil

Well, I recheck all about what you mentioned. Many thanks for help!

fpawel avatar Mar 18 '16 13:03 fpawel

I don't see what difference could make it fail on your side.

I have absolutely no clue about this

Are you using the latest WebSharper?

yes, of course

fpawel avatar Mar 18 '16 13:03 fpawel

Hello! Can you give a guess why this class does not serialize in JSON [NoEncodingException: No JSON encoding for LayoutObjects.LineDashMode]?

    [JavaScript, Serializable]
    public struct LineDashMode
    {
        public static class Mode
        {
            public const int Solid = 0;
            public const int Empty = 1;
            public const int Pattern = 2;
        }

        public readonly int mode;
        public readonly double[] spans;
        //public readonly int bitCount;

        private LineDashMode(int mode, double[] spans)
        {
            this.mode = mode;
            this.spans = spans;
        }

        public LineDashMode(double[] spans)
        {
            this.mode = Mode.Pattern;
            this.spans = spans;
        }

        public static readonly LineDashMode Solid = new LineDashMode(Mode.Solid, null);
        public static readonly LineDashMode Empty = new LineDashMode(Mode.Empty, null);

        public bool isPattern
        {
            get { return mode == Mode.Pattern; }
        }
        public bool isSolid
        {
            get { return mode == Mode.Solid; }
        }
        public bool isEmpty
        {
            get { return mode == Mode.Empty; }
        }
    }

Do the static fields interfere somehow?

V0d01ey avatar Feb 09 '18 12:02 V0d01ey

@V0d01ey Hi, sorry, error reporting should be improved here. I think what happens here is that WebSharper's serializer currently only looks for a parameterless constructor to create the object, then set the fields. (https://github.com/dotnet-websharper/core/blob/master/src/compiler/WebSharper.Core/Json.fs#L1245)

For now, just add a public LineDashMode() { }.

Jand42 avatar Feb 12 '18 10:02 Jand42

@Jand42 , does it mean that C# structs won't be serializable?

V0d01ey avatar Feb 12 '18 12:02 V0d01ey