Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 42 additions & 9 deletions src/stdlib/Json.fs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ type IExports =
/// See https://docs.python.org/3/library/json.html#json.dumps
[<NamedParams(fromIndex = 1)>]
abstract dumps: obj: obj * indent: int * ``default``: (obj -> obj) -> string
/// Serialize obj to a JSON formatted string with separators, ensure_ascii, and custom default
/// See https://docs.python.org/3/library/json.html#json.dumps
[<NamedParams(fromIndex = 1)>]
abstract dumps:
obj: obj * separators: string array * ensure_ascii: bool * ``default``: (obj -> obj) -> string
/// Serialize obj to a JSON formatted string with indent, separators, ensure_ascii, and custom default
/// See https://docs.python.org/3/library/json.html#json.dumps
[<NamedParams(fromIndex = 1)>]
abstract dumps:
obj: obj * indent: int * separators: string array * ensure_ascii: bool * ``default``: (obj -> obj) -> string
/// Deserialize a JSON document from a string to a Python object
/// See https://docs.python.org/3/library/json.html#json.loads
abstract loads: s: string -> obj
Expand Down Expand Up @@ -106,15 +116,38 @@ let fableDefault (o: obj) : obj =
else
raiseTypeError o

/// Serialize obj to JSON, automatically handling Fable types
let dumps (obj: obj) : string = json.dumps (obj, ``default`` = fableDefault)
/// Fable-aware JSON serialization with proper overloads.
/// Automatically handles Fable types (Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, Float32, Float64, unions, records).
[<Erase>]
type Json =
/// Serialize obj to JSON, automatically handling Fable types
static member inline dumps(obj: obj) : string =
json.dumps (obj, ``default`` = fableDefault)

/// Serialize obj to JSON with indentation, automatically handling Fable types
static member inline dumps(obj: obj, indent: int) : string =
json.dumps (obj, indent, ``default`` = fableDefault)

/// Serialize obj to JSON with indentation, automatically handling Fable types
let dumpsIndented (obj: obj) (indent: int) : string = json.dumps (obj, indent, ``default`` = fableDefault)
/// Serialize obj to JSON with custom separators and ensure_ascii, automatically handling Fable types
static member inline dumps(obj: obj, separators: string array, ensureAscii: bool) : string =
json.dumps (obj, separators = separators, ensure_ascii = ensureAscii, ``default`` = fableDefault)

/// Serialize obj as JSON stream to file, automatically handling Fable types
let dump (obj: obj) (fp: TextIOWrapper) : unit = json.dump (obj, fp, ``default`` = fableDefault)
/// Serialize obj to JSON with indentation, custom separators, and ensure_ascii, automatically handling Fable types
static member inline dumps(obj: obj, indent: int, separators: string array, ensureAscii: bool) : string =
json.dumps (obj, indent = indent, separators = separators, ensure_ascii = ensureAscii, ``default`` = fableDefault)

/// Serialize obj as JSON stream to file with indentation, automatically handling Fable types
let dumpIndented (obj: obj) (fp: TextIOWrapper) (indent: int) : unit =
json.dump (obj, fp, indent, ``default`` = fableDefault)
/// Serialize obj as JSON stream to file, automatically handling Fable types
static member inline dump(obj: obj, fp: TextIOWrapper) : unit =
json.dump (obj, fp, ``default`` = fableDefault)

/// Serialize obj as JSON stream to file with indentation, automatically handling Fable types
static member inline dump(obj: obj, fp: TextIOWrapper, indent: int) : unit =
json.dump (obj, fp, indent, ``default`` = fableDefault)

/// Deserialize a JSON document from a string to a Python object
static member inline loads(s: string) : obj =
json.loads s

/// Deserialize a JSON document from a file-like object to a Python object
static member inline load(fp: TextIOWrapper) : obj =
json.load fp
77 changes: 59 additions & 18 deletions test/TestJson.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,94 @@ module Fable.Python.Tests.Json
open Util.Testing
open Fable.Python.Json

[<Fact>]
let ``test works`` () =
let result = true
result |> equal true
// Test types for union and record serialization
type SimpleUnion =
| CaseA
| CaseB of int
| CaseC of string * int

type SimpleRecord = { Name: string; Age: int }

[<Fact>]
let ``test json dumps works`` () =
let ``test Json.dumps with anonymous record works`` () =
let object = {| A = 10; B = 20 |}
let result = dumps object
let result = Json.dumps object
result |> equal """{"A": 10, "B": 20}"""

[<Fact>]
let ``test json loads works`` () =
let ``test Json.loads works`` () =
let input = """{"Foo": 10, "Bar": "test"}"""
let object = {| Foo = 10; Bar = "test" |}
let result: {| Foo: int; Bar: string |} = unbox (json.loads input)
let result: {| Foo: int; Bar: string |} = unbox (Json.loads input)
result |> equal object

[<Fact>]
let ``test json.dumps with nativeint works`` () =
let ``test Json.dumps with int works`` () =
let value: int = 42
let result = Json.dumps value
result |> equal "42"

[<Fact>]
let ``test Json.dumps with nativeint works`` () =
let value: nativeint = 42n
let result = json.dumps value
let result = Json.dumps value
result |> equal "42"

[<Fact>]
let ``test json.dumps with ResizeArray of nativeint works`` () =
let ``test Json.dumps with ResizeArray works`` () =
let values = ResizeArray([ 1n; 2n; 3n ])
let result = json.dumps values
let result = Json.dumps values
result |> equal "[1, 2, 3]"

[<Fact>]
let ``test Json.dumps with ResizeArray of int works`` () =
let values = ResizeArray([ 1; 2; 3 ])
let result = Json.dumps values
result |> equal "[1, 2, 3]"

[<Fact>]
let ``test json.dumps with nested object works`` () =
let ``test Json.dumps with nested object works`` () =
let obj =
{| Name = "test"
Values = ResizeArray([ 1n; 2n; 3n ]) |}

let result = dumps obj
let result = Json.dumps obj
result |> equal """{"Name": "test", "Values": [1, 2, 3]}"""

[<Fact>]
let ``test json.loads with array works`` () =
let ``test Json.loads with array works`` () =
let input = "[1, 2, 3]"
let result: int array = unbox (json.loads input)
let result: int array = unbox (Json.loads input)
result.Length |> equal 3

[<Fact>]
let ``test json.dumps with indent works`` () =
let ``test Json.dumps with indent works`` () =
let obj = {| A = 1n |}
let result = dumpsIndented obj 2
let result = Json.dumps(obj, indent = 2)
result.Contains("\n") |> equal true

[<Fact>]
let ``test Json.dumps with record works`` () =
let record = { Name = "Alice"; Age = 30 }
let result = Json.dumps record
// Note: Fable records use lowercase slot names
result |> equal """{"name": "Alice", "age": 30}"""

[<Fact>]
let ``test Json.dumps with simple union case works`` () =
let union = CaseA
let result = Json.dumps union
// Note: Union cases without fields serialize as just the case name string
result |> equal "\"CaseA\""

[<Fact>]
let ``test Json.dumps with union case with single field works`` () =
let union = CaseB 42
let result = Json.dumps union
result |> equal """["CaseB", 42]"""

[<Fact>]
let ``test Json.dumps with union case with multiple fields works`` () =
let union = CaseC("hello", 123)
let result = Json.dumps union
result |> equal """["CaseC", "hello", 123]"""
Loading