Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fable vnext json de/serialisation issues and matching unions #473

Closed
7sharp9 opened this issue Oct 16, 2016 · 6 comments
Closed

Fable vnext json de/serialisation issues and matching unions #473

7sharp9 opened this issue Oct 16, 2016 · 6 comments

Comments

@7sharp9
Copy link
Collaborator

7sharp9 commented Oct 16, 2016

Description

Fable vnext de/serialisation issues

Repro steps

Please provide the steps required to reproduce the problem

The following code fails to deserialise:

#r "node_modules/fable-core/Fable.Core.dll"
open Fable.Import.Browser
module SharedTypes =
    type MemberClassification = 
        | Ctor = 0 | Member = 1 | Val = 2

    type TypeClassification =
        Type = 0 | Class = 1 | Union = 2 | Record = 3 | Struct = 4 | Enum = 5 | Interface = 6 | Measure = 7

    type SMember = string * MemberClassification * string

    type SType = { name : string ; ``type`` : TypeClassification; members : SMember list }

    type SModule = {name : string ; entities : Ents list }

    and SNamespace = { name:string ; entities : Ents list }

    and Ents =
        | Member of SMember
        | Type of SType
        | Module of SModule
        | Namespace of SNamespace

    type Assembly = {name : string; entities: Ents list}

open SharedTypes
let testMembers =
    [("test", MemberClassification.Ctor, "int -> unit")]

let ents =
    [unbox {name= "testType"; ``type`` = TypeClassification.Type; members = testMembers}]
let test = {name = "test"; entities = ents}

let json = Fable.Core.Serialize.toJson test
console.log(json)

let deserialiseTest = Fable.Core.Serialize.ofJson<Assembly> json
console.log(deserialiseTest)
match deserialiseTest.entities with
| head :: tail ->
    match head with
    | Type(t) ->
        match t.members with
        | head :: tail ->
            let a,b,c = head
            console.log(a)
            console.log(b)
            console.log(c)
        | _ -> console.log("here1")
    | _ -> console.log("here2")
| _ -> console.log("here3")

I have tried using Fable.JsonConverter to do the serialisation but that fails with a different error.

Expected behavior

deserialisation occurs successfully

Actual behavior

Fails with:

TypeError: Cannot read property 'length' of undefined
    at Function.inflate (/Users/7sharp9/fsharp/fableTest/node_modules/fable-core/fable-core.js:530:48)
    at /Users/7sharp9/fsharp/fableTest/node_modules/fable-core/fable-core.js:464:38
    at Array.map (native)
    at inflateArray (/Users/7sharp9/fsharp/fableTest/node_modules/fable-core/fable-core.js:463:76)
    at Function.inflate (/Users/7sharp9/fsharp/fableTest/node_modules/fable-core/fable-core.js:508:41)
    at Function.inflate (/Users/7sharp9/fsharp/fableTest/node_modules/fable-core/fable-core.js:548:48)
    at Function.ofJson (/Users/7sharp9/fsharp/fableTest/node_modules/fable-core/fable-core.js:573:30)
    at Object.<anonymous> (/Users/7sharp9/fsharp/fableTest/test.js:216:70)
    at Module._compile (module.js:541:32)
    at Object.Module._extensions..js (module.js:550:10)

Known workarounds

Manually edit js

Related information

  • Fable Version 0.7.4
  • Mono Version 4.6
  • Operating system: Osx sierra
@alfonsogarciacaro
Copy link
Member

Thanks for the sample, @7sharp9! I tried to run your code in F# interactive (changing console.log to Console.WriteLine and Newtonsoft.Json -without converters-) and it didn't run. However it did after changing unbox to Type as follows:

let ents =
    [Type {name= "testType"; ``type`` = TypeClassification.Type; members = testMembers}]

With this change I went back to Fable serialization, compiled to JS and it worked. Could you please try that too?

@7sharp9
Copy link
Collaborator Author

7sharp9 commented Oct 16, 2016

Hmm, yeah that works in the sample above, in the real code that fails there is no unbox though...

@7sharp9
Copy link
Collaborator Author

7sharp9 commented Oct 16, 2016

Is this what you used in to serialise?

let settings = JsonSerializerSettings(Converters = [|Fable.JsonConverter()|])
JsonConvert.SerializeObject(v, settings)

@alfonsogarciacaro
Copy link
Member

alfonsogarciacaro commented Oct 16, 2016

As the code listed above is serializing and deserializing, it works either if you use Fable.JsonConverter or not. This is the JSON produced with no converters:

{
    "name": "test",
    "entities": [
        {
            "Case": "Type",
            "Fields": [
                {
                    "name": "testType",
                    "type": 0,
                    "members": [
                        {
                            "Item1": "test",
                            "Item2": 0,
                            "Item3": "int -> unit"
                        }
                    ]
                }
            ]
        }
    ]
}

And this is with Fable.JsonConverter:

{
    "name": "test",
    "entities": [
        {
            "Type": {
                "name": "testType",
                "type": 0,
                "members": [
                    [
                        "test",
                        0,
                        "int -> unit"
                    ]
                ]
            }
        }
    ]
}

You mention the real code has not unbox and still fails. Could you please check that the proper type is passed to Fable.Core.Serialize.ofJson? (In the code above, because of type inference it was difficult to see entity was not actually of type Assembly.) If it still fails, could you please post or send me the code to reproduce the issue?

@7sharp9
Copy link
Collaborator Author

7sharp9 commented Oct 16, 2016

Essentially I think the serialisation was a red herring, the issue I was having was in a match block where the incorrect json parameters were being accessed.

screen shot 2016-10-16 at 10 41 19

The js code produced was this:

function processEntity(sb, indent, entity) {
    if (entity.Case === "Type") {
        sb.AppendLine_0(_fableCore.String.fsFormat("<p style=\"margin-left: %ipx\">")(function (x) {
            return x;
        })(indent));
        sb.AppendLine_0(SharedTypes.typeClassificationToString(entity.Fields[0].type) + " " + entity.Fields[0].name);
        sb.AppendLine_0(_fableCore.String.fsFormat("<p style=\"margin-left: %ipx\">")(function (x) {
            return x;
        })(indent + 20));

        _fableCore.Seq.iterate(function () {
            var indent_1 = indent + 20;
            return function (entity_1) {
                processEntity(sb, indent_1, entity_1);
            };
        }(), entity.Fields[0].members);

        sb.AppendLine_0("</p>");
        sb.AppendLine_0("</p>");
    } else {
        if (entity.Case === "Module") {
            sb.AppendLine_0(_fableCore.String.fsFormat("<p style=\"margin-left: %ipx\">")(function (x) {
                return x;
            })(indent));
            sb.AppendLine_0("module " + entity.Fields[0].name);

            _fableCore.Seq.iterate(function () {
                var indent_1 = indent + 20;
                return function (entity_1) {
                    processEntity(sb, indent_1, entity_1);
                };
            }(), entity.Fields[0].entities);

            sb.AppendLine_0("</p>");
        } else {
            if (entity.Case === "Namespace") {
                sb.AppendLine_0(_fableCore.String.fsFormat("<p style=\"margin-left: %ipx\">")(function (x) {
                    return x;
                })(indent));
                sb.AppendLine_0("namespace " + entity.Fields[0].name);

                _fableCore.Seq.iterate(function () {
                    var indent_1 = indent + 20;
                    return function (entity_1) {
                        processEntity(sb, indent_1, entity_1);
                    };
                }(), entity.Fields[0].entities);

                sb.AppendLine_0("</p>");
            } else {
                var typ = entity.Fields[0][1];
                var signature = entity.Fields[0][2];
                var name = entity.Fields[0][0];
                sb.AppendLine_0(name + " : " + signature);
                sb.AppendLine_0("<br>");
            }
        }
    }
}

The interesting section was this:

else {
                var typ = entity.Fields[0][1];
                var signature = entity.Fields[0][2];
                var name = entity.Fields[0][0];
                sb.AppendLine_0(name + " : " + signature);
                sb.AppendLine_0("<br>");
            }

Which should of been like this:

else {
                var typ = entity[1];
                var signature = entity[2];
                var name = entity[0];
                sb.AppendLine_0(name + " : " + signature);
                sb.AppendLine_0("<br>");
            }

@7sharp9 7sharp9 changed the title Fable vnext json de/serialisation issues Fable vnext json de/serialisation issues and matching unions Oct 16, 2016
@alfonsogarciacaro
Copy link
Member

As we talked, the produced JS code is fine because Fable always represents unions at runtime as { Case: string, Fields: any[] } objects (no matter how they're serialized). The error there seems to be caused because entity is not well-formed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants