Fable: BigInt does not have toJSON implementation

Created on 18 Dec 2018  路  7Comments  路  Source: fable-compiler/Fable

Description

Stringifying bigint to JSON using the following function:

let stringify (value: 'a) : string =
    JS.JSON.stringify(value, (fun _ v ->
        match v with
        | :? string -> v
        | :? System.Collections.IEnumerable ->
            if JS.Array.isArray(v) then v
            else JS.Array.from(v :?> JS.Iterable<obj>) |> box
        | _ -> v
    ), 0)

for example, stringify 5I will return this:

{"signInt":1,"v":{"bound":1,"digits":[5]}}

instead of

"5"

Related information

  • Fable version: fable-compiler 2.1.8
  • Operating system: Windows 10
backlog discussion

All 7 comments

Yes, for the fable-library types defined in JS/TS, we add toJson to the prototype, but we haven't done it yet for types defined in F#. I guess we should have a IJsonSerializable interface with a toJson method (lowercase) and make the types implement it so we are sure the member is attached to the prototype.

@alfonsogarciacaro I think I might have found a workaround but it is pretty ugly, testing for the existence of internal properties of the compiled BigInt, same for DateTimeOffset (workaround for #1710):

[<AutoOpen>]
module InteropUtil =    
    [<Emit("$1[$0]")>]
    let get<'a> (key: string) (x: obj) : 'a = jsNative
    [<Emit("$0 instanceof Date")>]
    let isDate (x: obj) = jsNative
    [<Emit("$0 in $1")>]
    let hasKey (key: string) (x: 'a) = jsNative
    let isDateOffset (x: obj) = isDate x && hasKey "offset" x
    let isObjectLiteral (x: obj) = getTypeOf x = "object" 

    let isBigInt (x: obj) = 
        not (isNull x)
        && isObjectLiteral x
        && hasKey "signInt" x 
        && hasKey "v" x 
        && hasKey "digits" (get "v" x)
        && hasKey "bound" (get "v" x)

    let stringify (value: 'a) : string =
        JS.JSON.stringify(value, (fun key v ->
            if isDateOffset (get key jsThis) then 
                let dateOffset : DateTimeOffset = get key jsThis
                box (dateOffset.ToString("O"))
            elif isBigInt (get key jsThis) then 
                let bigInt : bigint = get key jsThis
                box (string (decimal bigInt))
            else 
            match v with
            | :? string -> v
            | :? System.Collections.IEnumerable ->
                if JS.Array.isArray(v) then v
                else JS.Array.from(v :?> JS.Iterable<obj>) |> box
            | _ when isBigInt v -> box (string (decimal (unbox<bigint> v)))
            | _ when isDateOffset v -> box ((unbox<DateTimeOffset> v).ToString("O"))
            | _ -> v
        ), 0)

Another little problem is that calling string or ToString() on bigint result in "[object Object]" (that's why I am converting to decimal first, probably needs a separate issue)

Hmm, actually converting the bigint to string should be the best way to serialize it to JSON. ToString with bigints is working fine in the REPL, either boxed or unboxed. I don't understand why you're getting "[object Object]" 馃槙

I might not had latest fable when I ran the tests, I will check :)

@Zaid-Ajaj Can this be closed?

I have fixed the problem by using reflection, so for me it is fixed in SimpleJson but if someone was to call JSON.stringify on big integers, they would get unexpected results. I think it is still a good idea to implement toJSON out of the box for these integers

Hopefully fixed by https://github.com/fable-compiler/Fable/commit/20a6d4555caceb9fa89718402c6d99d4787cc9ad. Will be released with 3.1.12, please reopen if it's still an issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

forki picture forki  路  3Comments

alfonsogarciacaro picture alfonsogarciacaro  路  3Comments

funlambda picture funlambda  路  4Comments

alfonsogarciacaro picture alfonsogarciacaro  路  3Comments

forki picture forki  路  3Comments