From 4fe60bf1daf5ad0c007f1e16647570d1178b4a54 Mon Sep 17 00:00:00 2001 From: Dmitry Zakharov Date: Sat, 11 May 2024 15:33:50 +0400 Subject: [PATCH] Experiment with fast json stringify --- packages/tests/src/benchmark/Benchmark.bs.mjs | 79 ++++++++++++----- packages/tests/src/benchmark/Benchmark.res | 40 +++++++++ packages/tests/src/core/S_jsonString_test.res | 40 ++++++++- src/S_Core.bs.mjs | 77 ++++++++++++---- src/S_Core.res | 88 +++++++++++++++---- 5 files changed, 267 insertions(+), 57 deletions(-) diff --git a/packages/tests/src/benchmark/Benchmark.bs.mjs b/packages/tests/src/benchmark/Benchmark.bs.mjs index 7683767e..223de60a 100644 --- a/packages/tests/src/benchmark/Benchmark.bs.mjs +++ b/packages/tests/src/benchmark/Benchmark.bs.mjs @@ -13,6 +13,14 @@ function run(suite) { })).run(); } +function makeNestedTestObject() { + return (Object.freeze({ + foo: 'bar', + num: 1, + bool: false, + })); +} + function makeTestObject() { return (Object.freeze({ number: 1, @@ -30,6 +38,16 @@ function makeTestObject() { })); } +function makeNestedSchema() { + return S$RescriptSchema.object(function (s) { + return { + foo: s.f("foo", S$RescriptSchema.string), + num: s.f("num", S$RescriptSchema.$$float), + bool: s.f("bool", S$RescriptSchema.bool) + }; + }); +} + function makeAdvancedObjectSchema() { return S$RescriptSchema.object(function (s) { return { @@ -114,37 +132,54 @@ S$RescriptSchema.serializeWith(data, schema); console.timeEnd("s: 3"); -run(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(new (Benchmark.default.Suite)(), "Parse string", (function () { - return function () { - return S$RescriptSchema.parseAnyOrRaiseWith("Hello world!", S$RescriptSchema.string); - }; - })), "Serialize string", (function () { - return function () { - return S$RescriptSchema.serializeOrRaiseWith("Hello world!", S$RescriptSchema.string); - }; - })).add("Advanced object schema factory", makeAdvancedObjectSchema), "Parse advanced object", (function () { +run(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(new (Benchmark.default.Suite)(), "Parse string", (function () { + return function () { + return S$RescriptSchema.parseAnyOrRaiseWith("Hello world!", S$RescriptSchema.string); + }; + })), "Serialize string", (function () { + return function () { + return S$RescriptSchema.serializeOrRaiseWith("Hello world!", S$RescriptSchema.string); + }; + })).add("Advanced object schema factory", makeAdvancedObjectSchema), "Parse advanced object", (function () { + var schema = makeAdvancedObjectSchema(); + var data = makeTestObject(); + return function () { + return S$RescriptSchema.parseAnyOrRaiseWith(data, schema); + }; + })), "Create and parse advanced object", (function () { + var data = makeTestObject(); + return function () { + var schema = makeAdvancedObjectSchema(); + return S$RescriptSchema.parseAnyOrRaiseWith(data, schema); + }; + })), "Parse advanced strict object", (function () { + var schema = makeAdvancedStrictObjectSchema(); + var data = makeTestObject(); + return function () { + return S$RescriptSchema.parseAnyOrRaiseWith(data, schema); + }; + })), "Serialize advanced object", (function () { var schema = makeAdvancedObjectSchema(); var data = makeTestObject(); return function () { - return S$RescriptSchema.parseAnyOrRaiseWith(data, schema); + return S$RescriptSchema.serializeOrRaiseWith(data, schema); }; - })), "Create and parse advanced object", (function () { - var data = makeTestObject(); + })), "Stringify with JSON.stringify", (function () { + var data = makeNestedTestObject(); return function () { - var schema = makeAdvancedObjectSchema(); - return S$RescriptSchema.parseAnyOrRaiseWith(data, schema); + return JSON.stringify(data); }; - })), "Parse advanced strict object", (function () { - var schema = makeAdvancedStrictObjectSchema(); - var data = makeTestObject(); + })), "Stringify with S.serializeToJsonStringWith", (function () { + var data = makeNestedTestObject(); + var schema = makeNestedSchema(); return function () { - return S$RescriptSchema.parseAnyOrRaiseWith(data, schema); + return S$RescriptSchema.serializeToJsonStringWith(data, schema, undefined); }; - })), "Serialize advanced object", (function () { - var schema = makeAdvancedObjectSchema(); - var data = makeTestObject(); + })), "Stringify with S.jsonString", (function () { + var data = makeNestedTestObject(); + var schema = S$RescriptSchema.jsonString(makeNestedSchema(), undefined); return function () { - return S$RescriptSchema.serializeOrRaiseWith(data, schema); + return S$RescriptSchema.serializeToUnknownWith(data, schema); }; }))); diff --git a/packages/tests/src/benchmark/Benchmark.res b/packages/tests/src/benchmark/Benchmark.res index 9a08de90..599faa9a 100644 --- a/packages/tests/src/benchmark/Benchmark.res +++ b/packages/tests/src/benchmark/Benchmark.res @@ -36,6 +36,14 @@ module Suite = { } } +let makeNestedTestObject = () => { + %raw(`Object.freeze({ + foo: 'bar', + num: 1, + bool: false, + })`) +} + let makeTestObject = () => { %raw(`Object.freeze({ number: 1, @@ -53,6 +61,15 @@ let makeTestObject = () => { })`) } +let makeNestedSchema = () => + S.object(s => + { + "foo": s.field("foo", S.string), + "num": s.field("num", S.float), + "bool": s.field("bool", S.bool), + } + ) + let makeAdvancedObjectSchema = () => { S.object(s => { @@ -166,4 +183,27 @@ Suite.make() data->S.serializeOrRaiseWith(schema) } }) +// V6.2 x 277,905 ops/sec +->Suite.addWithPrepare("Stringify with JSON.stringify", () => { + let data = makeNestedTestObject() + () => { + data->Js.Json.stringifyAny + } +}) +// V6.2 x 277,250 ops/sec +->Suite.addWithPrepare("Stringify with S.serializeToJsonStringWith", () => { + let data = makeNestedTestObject() + let schema = makeNestedSchema() + () => { + data->S.serializeToJsonStringWith(schema) + } +}) +// V6.2 x 277,401 ops/sec +->Suite.addWithPrepare("Stringify with S.jsonString", () => { + let data = makeNestedTestObject() + let schema = S.jsonString(makeNestedSchema()) + () => { + data->S.serializeToUnknownWith(schema) + } +}) ->Suite.run diff --git a/packages/tests/src/core/S_jsonString_test.res b/packages/tests/src/core/S_jsonString_test.res index 9740a442..5cd104da 100644 --- a/packages/tests/src/core/S_jsonString_test.res +++ b/packages/tests/src/core/S_jsonString_test.res @@ -7,7 +7,7 @@ test("Successfully parses JSON", t => { t->Assert.deepEqual(`"Foo"`->S.parseAnyWith(S.jsonString(schema)), Ok("Foo"), ()) }) -test("Successfully serializes JSON", t => { +test("Successfully serializes string to JSON", t => { let schema = S.string t->Assert.deepEqual( @@ -17,6 +17,36 @@ test("Successfully serializes JSON", t => { ) }) +test("Successfully serializes string literal to JSON", t => { + let schema = S.literal("foo") + + t->Assert.deepEqual( + `foo`->S.serializeToUnknownWith(S.jsonString(schema)), + Ok(%raw(`'"foo"'`)), + (), + ) +}) + +test("Successfully serializes float to JSON", t => { + let schema = S.float + + t->Assert.deepEqual( + 123.4->S.serializeToUnknownWith(S.jsonString(schema)), + Ok(%raw(`'123.4'`)), + (), + ) +}) + +test("Successfully serializes tuple", t => { + let schema = S.tuple2(S.int, S.string) + + t->Assert.deepEqual( + (12, "foo")->S.serializeToUnknownWith(S.jsonString(schema)), + Ok(%raw(`'[12,"foo"]'`)), + (), + ) +}) + test("Successfully serializes JSON object", t => { let schema = S.schema(_ => { @@ -33,6 +63,12 @@ test("Successfully serializes JSON object", t => { Ok(%raw(`'{"foo":"bar","baz":[1,3]}'`)), (), ) + t->U.assertCompiledCode( + ~schema=S.jsonString(schema), + ~op=#serialize, + // FIXME: Checks?? + `i=>{return \'{"foo":\'+"\\"bar\\""+\',"baz":\'+JSON.stringify(i["baz"])+\'}\'}`, + ) }) test("Successfully serializes JSON object with space", t => { @@ -88,7 +124,7 @@ test("Compiled async parse code snapshot", t => { test("Compiled serialize code snapshot", t => { let schema = S.jsonString(S.bool) - t->U.assertCompiledCode(~schema, ~op=#serialize, `i=>{return JSON.stringify(i)}`) + t->U.assertCompiledCode(~schema, ~op=#serialize, `i=>{return (i?"true":"false")}`) }) test("Compiled serialize code snapshot with space", t => { diff --git a/src/S_Core.bs.mjs b/src/S_Core.bs.mjs index f6f7c6d1..f068b102 100644 --- a/src/S_Core.bs.mjs +++ b/src/S_Core.bs.mjs @@ -683,6 +683,10 @@ function validateJsonableSchema(_schema, rootSchema, _isRootOpt) { }; } +function defaultToJsonString(input) { + return "JSON.stringify(" + input + ")"; +} + function unexpectedAsync(param) { throw new RescriptSchemaError("UnexpectedAsync", "Parsing", ""); } @@ -949,6 +953,7 @@ function set$1(schema, id, metadata) { p: schema.p, s: schema.s, f: schema.f, + j: schema.j, i: 0, m: metadataMap }; @@ -1031,6 +1036,7 @@ function setName(schema, name) { p: schema.p, s: schema.s, f: schema.f, + j: schema.j, i: 0, m: schema.m }; @@ -1066,6 +1072,7 @@ function internalRefine(schema, refiner) { })), path); }), f: schema.f, + j: schema.j, i: 0, m: schema.m }; @@ -1121,6 +1128,7 @@ function transform$1(schema, transformer) { } }), f: schema.f, + j: schema.j, i: 0, m: schema.m }; @@ -1186,7 +1194,6 @@ function preprocess(schema, transformer) { return input$1; } }), - f: undefined, i: 0, m: schema.m }; @@ -1230,7 +1237,6 @@ function custom(name, definer) { return input; } }), - f: undefined, i: 0, m: empty }; @@ -1249,6 +1255,15 @@ function literal(value) { }), inputVar) + ";"); return inputVar; }; + var tmp; + if (literal$1.j) { + var string = JSON.stringify(literal$1.s); + tmp = (function (param) { + return string; + }); + } else { + tmp = undefined; + } return { t: { TAG: "Literal", @@ -1259,7 +1274,7 @@ function literal(value) { }), p: operationBuilder, s: operationBuilder, - f: undefined, + j: tmp, i: 0, m: empty }; @@ -1342,6 +1357,7 @@ function factory(schema, definer) { } }), f: schema.f, + j: schema.j, i: 0, m: schema.m }; @@ -1452,6 +1468,7 @@ function factory$2(schema) { p: parseOperationBuilder, s: serializeOperationBuilder, f: maybeTypeFilter(schema, "null"), + j: defaultToJsonString, i: 0, m: empty }; @@ -1686,6 +1703,16 @@ function factory$3(definer) { return "{" + fieldsCodeRef.contents + "}"; }), f: typeFilter, + j: (function (input) { + var jsonStringRef = "'{"; + for(var idx = 0 ,idx_finish = itemDefinitions.length; idx < idx_finish; ++idx){ + var itemDefinition = itemDefinitions[idx]; + jsonStringRef = jsonStringRef + ( + idx === 0 ? "" : "," + ) + itemDefinition.l + ":'+" + itemDefinition.s.j(input + "[" + itemDefinition.l + "]") + "+'"; + } + return jsonStringRef + "}'"; + }), i: 0, m: empty }; @@ -1707,6 +1734,7 @@ function strip(schema) { p: schema.p, s: schema.s, f: schema.f, + j: schema.j, i: 0, m: schema.m }; @@ -1729,6 +1757,7 @@ function strict(schema) { p: schema.p, s: schema.s, f: schema.f, + j: schema.j, i: 0, m: schema.m }; @@ -1752,7 +1781,7 @@ var schema = { n: primitiveName, p: builder, s: builder, - f: undefined, + j: defaultToJsonString, i: 0, m: empty }; @@ -1762,7 +1791,6 @@ var schema$1 = { n: primitiveName, p: noop, s: noop, - f: undefined, i: false, m: empty }; @@ -1796,6 +1824,7 @@ var schema$2 = { p: noop, s: noop, f: typeFilter$1, + j: defaultToJsonString, i: 0, m: empty }; @@ -1929,12 +1958,11 @@ function trim(schema) { function factory$4(schema, spaceOpt) { var space = spaceOpt !== undefined ? spaceOpt : 0; - try { - validateJsonableSchema(schema, schema, true); - } - catch (raw_exn){ - var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); - getOrRethrow(exn); + var v = schema.j; + var toJsonString; + if (v !== undefined) { + toJsonString = v; + } else { var message = "The schema " + schema.n() + " passed to S.jsonString is not compatible with JSON"; throw new Error("[rescript-schema] " + message); } @@ -1954,11 +1982,14 @@ function factory$4(schema, spaceOpt) { }), s: (function (b, param, path) { var input = b.i; - return "JSON.stringify(" + use(b, schema, input, path) + ( - space > 0 ? ",null," + space : "" - ) + ")"; + if (space !== 0) { + return "JSON.stringify(" + use(b, schema, input, path) + ",null," + space + ")"; + } else { + return toJsonString(input); + } }), f: typeFilter$1, + j: defaultToJsonString, i: 0, m: empty }; @@ -1974,6 +2005,9 @@ var schema$3 = { p: noop, s: noop, f: typeFilter$2, + j: (function (input) { + return "(" + input + "?\"true\":\"false\")"; + }), i: 0, m: empty }; @@ -1993,12 +2027,17 @@ function typeFilter$3(inputVar) { return "typeof " + inputVar + "!==\"number\"||" + inputVar + ">2147483647||" + inputVar + "<-2147483648||" + inputVar + "%1!==0"; } +function toJsonString(input) { + return input + ".toString()"; +} + var schema$4 = { t: "Int", n: primitiveName, p: noop, s: noop, f: typeFilter$3, + j: toJsonString, i: 0, m: empty }; @@ -2060,6 +2099,7 @@ var schema$5 = { p: noop, s: noop, f: typeFilter$4, + j: toJsonString, i: 0, m: empty }; @@ -2146,6 +2186,7 @@ function factory$5(schema) { return outputVar; }), f: typeFilter$5, + j: defaultToJsonString, i: 0, m: empty }; @@ -2235,6 +2276,7 @@ function factory$6(schema) { return outputVar; }), f: typeFilter, + j: defaultToJsonString, i: 0, m: empty }; @@ -2362,6 +2404,7 @@ function factory$7(definer) { return outputVar; }), f: typeFilter$5, + j: defaultToJsonString, i: 0, m: empty }; @@ -2482,7 +2525,7 @@ function factory$8(schemas) { }), "[" + errorVarsRef + "]") + codeEndRef; return outputVar; }), - f: undefined, + j: defaultToJsonString, i: 0, m: empty }; @@ -2555,7 +2598,7 @@ function json(validate) { return "e[" + (b.e.push(parse) - 1) + "](" + input + ")"; }) : noop, s: noop, - f: undefined, + j: defaultToJsonString, i: 0, m: empty }; @@ -2589,7 +2632,7 @@ function $$catch(schema, getFallbackValue) { })); }), s: schema.s, - f: undefined, + j: schema.j, i: 0, m: schema.m }; diff --git a/src/S_Core.res b/src/S_Core.res index 094f8470..4754305e 100644 --- a/src/S_Core.res +++ b/src/S_Core.res @@ -244,7 +244,9 @@ type rec t<'value> = { @as("s") mutable serializeOperationBuilder: builder, @as("f") - maybeTypeFilter: option<(~inputVar: string) => string>, + maybeTypeFilter?: (~inputVar: string) => string, + @as("j") + maybeToJsonString?: string => string, @as("i") mutable isAsyncParse: isAsyncParse, @as("m") @@ -1066,12 +1068,14 @@ let make = ( ~parseOperationBuilder, ~serializeOperationBuilder, ~maybeTypeFilter, + ~toJsonString=?, ) => { tagged, parseOperationBuilder, serializeOperationBuilder, isAsyncParse: Unknown, - maybeTypeFilter, + ?maybeTypeFilter, + maybeToJsonString: ?toJsonString, name, metadataMap, } @@ -1083,16 +1087,20 @@ let makeWithNoopSerializer = ( ~metadataMap, ~parseOperationBuilder, ~maybeTypeFilter, + ~toJsonString=?, ) => { name, tagged, parseOperationBuilder, serializeOperationBuilder: Builder.noop, isAsyncParse: Unknown, - maybeTypeFilter, + ?maybeTypeFilter, + maybeToJsonString: ?toJsonString, metadataMap, } +let defaultToJsonString = input => `JSON.stringify(${input})` + module Operation = { let unexpectedAsync = _ => InternalError.raise(~path=Path.empty, ~code=UnexpectedAsync, ~operation=Parsing) @@ -1278,6 +1286,7 @@ module Metadata = { ~serializeOperationBuilder=schema.serializeOperationBuilder, ~tagged=schema.tagged, ~maybeTypeFilter=schema.maybeTypeFilter, + ~toJsonString=?schema.maybeToJsonString, ~metadataMap, ) } @@ -1369,6 +1378,7 @@ let setName = (schema, name) => { ~serializeOperationBuilder=schema.serializeOperationBuilder, ~tagged=schema.tagged, ~maybeTypeFilter=schema.maybeTypeFilter, + ~toJsonString=?schema.maybeToJsonString, ~metadataMap=schema.metadataMap, ) } @@ -1408,6 +1418,7 @@ let internalRefine = (schema, refiner) => { ) }), ~maybeTypeFilter=schema.maybeTypeFilter, + ~toJsonString=?schema.maybeToJsonString, ~metadataMap=schema.metadataMap, ) } @@ -1476,6 +1487,7 @@ let transform: (t<'input>, s<'output> => transformDefinition<'input, 'output>) = } }), ~maybeTypeFilter=schema.maybeTypeFilter, + ~toJsonString=?schema.maybeToJsonString, ~metadataMap=schema.metadataMap, ) } @@ -1621,6 +1633,13 @@ let literal = value => { ~parseOperationBuilder=operationBuilder, ~serializeOperationBuilder=operationBuilder, ~maybeTypeFilter=None, + ~toJsonString=?switch literal->Literal.isJsonable { + | true => { + let string = literal->Literal.toString->Stdlib.Inlined.Value.fromString + Some(_ => string) + } + | false => None + }, ) } let unit = literal(%raw("void 0")) @@ -1750,6 +1769,7 @@ module Variant = { } }), ~maybeTypeFilter=schema.maybeTypeFilter, + ~toJsonString=?schema.maybeToJsonString, ~metadataMap=schema.metadataMap, ) } @@ -1870,6 +1890,7 @@ module Null = { ~parseOperationBuilder=Option.parseOperationBuilder, ~serializeOperationBuilder=Option.serializeOperationBuilder, ~maybeTypeFilter=Option.maybeTypeFilter(~schema, ~inlinedNoneValue="null"), + ~toJsonString=defaultToJsonString, // FIXME: ) } } @@ -2220,6 +2241,23 @@ module Object = { `{${fieldsCodeRef.contents}}` }), ~maybeTypeFilter=Some(typeFilter), + ~toJsonString=input => { + let jsonStringRef = ref(`'{`) + for idx in 0 to itemDefinitions->Js.Array2.length - 1 { + let itemDefinition = itemDefinitions->Js.Array2.unsafe_get(idx) + jsonStringRef.contents = + jsonStringRef.contents ++ + (idx === 0 ? `` : `,`) ++ + itemDefinition.inlinedInputLocation ++ + `:` ++ + `'+` ++ + (itemDefinition.schema.maybeToJsonString->(Obj.magic: option<'a> => 'a))( + `${input}[${itemDefinition.inlinedInputLocation}]`, + ) ++ `+'` + } + jsonStringRef.contents ++ `}'` + }, + // ~toJsonString=defaultToJsonString, // FIXME: ) } @@ -2232,6 +2270,7 @@ module Object = { ~parseOperationBuilder=schema.parseOperationBuilder, ~serializeOperationBuilder=schema.serializeOperationBuilder, ~maybeTypeFilter=schema.maybeTypeFilter, + ~toJsonString=?schema.maybeToJsonString, ~metadataMap=schema.metadataMap, ) | _ => schema @@ -2247,6 +2286,7 @@ module Object = { ~parseOperationBuilder=schema.parseOperationBuilder, ~serializeOperationBuilder=schema.serializeOperationBuilder, ~maybeTypeFilter=schema.maybeTypeFilter, + ~toJsonString=?schema.maybeToJsonString, ~metadataMap=schema.metadataMap, ) // TODO: Should it throw for non Object schemas? @@ -2278,6 +2318,7 @@ module Never = { ~parseOperationBuilder=builder, ~serializeOperationBuilder=builder, ~maybeTypeFilter=None, + ~toJsonString=defaultToJsonString, ) } @@ -2289,7 +2330,6 @@ module Unknown = { serializeOperationBuilder: Builder.noop, isAsyncParse: Value(false), metadataMap: Metadata.Map.empty, - maybeTypeFilter: None, } } @@ -2338,6 +2378,7 @@ module String = { ~tagged=String, ~parseOperationBuilder=Builder.noop, ~maybeTypeFilter=Some(typeFilter), + ~toJsonString=defaultToJsonString, ) let min = (schema, length, ~message as maybeMessage=?) => { @@ -2494,15 +2535,12 @@ module String = { module JsonString = { let factory = (schema, ~space=0) => { let schema = schema->toUnknown - try { - schema->validateJsonableSchema(~rootSchema=schema, ~isRoot=true) - } catch { - | exn => { - let _ = exn->InternalError.getOrRethrow - InternalError.panic( - `The schema ${schema.name()} passed to S.jsonString is not compatible with JSON`, - ) - } + let toJsonString = switch schema.maybeToJsonString { + | Some(v) => v + | None => + InternalError.panic( + `The schema ${schema.name()} passed to S.jsonString is not compatible with JSON`, + ) } make( ~name=primitiveName, @@ -2523,11 +2561,18 @@ module JsonString = { }), ~serializeOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { let input = b->B.useInput - `JSON.stringify(${b->B.use(~schema, ~input, ~path)}${space > 0 - ? `,null,${space->Stdlib.Int.unsafeToString}` - : ""})` + switch space { + | 0 => toJsonString(input) + | _ => + `JSON.stringify(${b->B.use( + ~schema, + ~input, + ~path, + )},null,${space->Stdlib.Int.unsafeToString})` + } }), ~maybeTypeFilter=Some(String.typeFilter), + ~toJsonString=defaultToJsonString, ) } } @@ -2541,6 +2586,7 @@ module Bool = { ~tagged=Bool, ~parseOperationBuilder=Builder.noop, ~maybeTypeFilter=Some(typeFilter), + ~toJsonString=input => `(${input}?"true":"false")`, ) } @@ -2571,12 +2617,15 @@ module Int = { let typeFilter = (~inputVar) => `typeof ${inputVar}!=="number"||${inputVar}>2147483647||${inputVar}<-2147483648||${inputVar}%1!==0` + let toJsonString = input => `${input}.toString()` + let schema = makeWithNoopSerializer( ~name=primitiveName, ~metadataMap=Metadata.Map.empty, ~tagged=Int, ~parseOperationBuilder=Builder.noop, ~maybeTypeFilter=Some(typeFilter), + ~toJsonString, ) let min = (schema, minValue, ~message as maybeMessage=?) => { @@ -2658,6 +2707,7 @@ module Float = { ~tagged=Float, ~parseOperationBuilder=Builder.noop, ~maybeTypeFilter=Some(typeFilter), + ~toJsonString=Int.toJsonString, ) let min = (schema, minValue, ~message as maybeMessage=?) => { @@ -2782,6 +2832,7 @@ module Array = { } }), ~maybeTypeFilter=Some(typeFilter), + ~toJsonString=defaultToJsonString, // FIXME: ) } @@ -2902,6 +2953,7 @@ module Dict = { } }), ~maybeTypeFilter=Some(Object.typeFilter), + ~toJsonString=defaultToJsonString, // FIXME: ) } } @@ -3091,6 +3143,7 @@ module Tuple = { }), ~maybeTypeFilter=Some(Array.typeFilter), ~metadataMap=Metadata.Map.empty, + ~toJsonString=defaultToJsonString, // FIXME: ) } } @@ -3256,6 +3309,7 @@ module Union = { outputVar }), ~maybeTypeFilter=None, + ~toJsonString=defaultToJsonString, // FIXME: ) } } @@ -3276,6 +3330,7 @@ let json = (~validate) => ~tagged=JSON({validated: validate}), ~metadataMap=Metadata.Map.empty, ~maybeTypeFilter=None, + ~toJsonString=defaultToJsonString, ~parseOperationBuilder=validate ? Builder.make((b, ~selfSchema, ~path) => { let rec parse = (input, ~path=path) => { @@ -3375,6 +3430,7 @@ let catch = (schema, getFallbackValue) => { ~serializeOperationBuilder=schema.serializeOperationBuilder, ~tagged=schema.tagged, ~maybeTypeFilter=None, + ~toJsonString=?schema.maybeToJsonString, ~metadataMap=schema.metadataMap, ) }