|
1 | 1 | namespace FSharp.Data.GraphQL.Server.Middleware |
2 | 2 |
|
3 | 3 | open System |
| 4 | +open System.Buffers |
4 | 5 | open System.Collections.Generic |
5 | 6 | open System.Reflection |
6 | 7 | open System.Text.Json |
@@ -102,39 +103,57 @@ module TypeCoercion = |
102 | 103 | |> ValueOption.defaultValue fieldName |
103 | 104 |
|
104 | 105 | /// <summary> |
105 | | - /// Converts a boxed GraphQL scalar primitive to its JSON text representation so that <see cref="JsonSerializer.Deserialize"/> can produce the |
106 | | - /// correct CLR value. Strings and enum-like values are JSON-quoted (<c>"value"</c>); numbers and booleans are emitted as raw JSON tokens. |
| 106 | + /// Writes a boxed GraphQL scalar primitive as a JSON token directly into <paramref name="writer"/>. Strings become JSON strings; numbers and |
| 107 | + /// booleans become raw JSON tokens. Returns <c>true</c> if the value was written; <c>false</c> if the type is unsupported. |
107 | 108 | /// </summary> |
108 | | - let private toJsonString (value : obj) : string voption = |
| 109 | + let private writeJsonValue (value : obj) (writer : Utf8JsonWriter) : bool = |
109 | 110 | match value with |
110 | | - | :? string as s -> ValueSome $"\"{JsonEncodedText.Encode(s)}\"" |
111 | | - | :? bool as b -> ValueSome (if b then "true" else "false") |
112 | | - | :? int64 as n -> ValueSome (string n) |
113 | | - | :? int as n -> ValueSome (string n) |
114 | | - | :? double as n -> ValueSome (sprintf "%g" n) |
115 | | - | :? float32 as n -> ValueSome (sprintf "%g" n) |
116 | | - | :? decimal as n -> ValueSome (string n) |
117 | | - | _ -> ValueNone |
| 111 | + | :? string as s -> |
| 112 | + writer.WriteStringValue s |
| 113 | + true |
| 114 | + | :? bool as b -> |
| 115 | + writer.WriteBooleanValue b |
| 116 | + true |
| 117 | + | :? int64 as n -> |
| 118 | + writer.WriteNumberValue n |
| 119 | + true |
| 120 | + | :? int as n -> |
| 121 | + writer.WriteNumberValue n |
| 122 | + true |
| 123 | + | :? double as n -> |
| 124 | + writer.WriteNumberValue n |
| 125 | + true |
| 126 | + | :? float32 as n -> |
| 127 | + writer.WriteNumberValue n |
| 128 | + true |
| 129 | + | :? decimal as n -> |
| 130 | + writer.WriteNumberValue n |
| 131 | + true |
| 132 | + | _ -> |
| 133 | + false |
118 | 134 |
|
119 | 135 | // Suppress nullness warnings for the obj / objnull mixture. |
120 | 136 | #nowarn "3261" |
121 | 137 | /// <summary> |
122 | | - /// Tries to coerce a value into <paramref name="targetType"/> using STJ deserialization. Primitives are first rendered as a JSON string via |
123 | | - /// <see cref="toJsonString"/> then parsed with <see cref="JsonDocument.Parse"/> and deserialized. Already-correct values pass through unchanged. |
| 138 | + /// Tries to coerce a value into <paramref name="targetType"/> using STJ deserialization. Primitives are written directly as JSON bytes via |
| 139 | + /// <see cref="writeJsonValue"/> into an <see cref="ArrayBufferWriter{T}"/>, then deserialized from <c>ReadOnlySpan<byte></c>. |
| 140 | + /// Already-correct values pass through unchanged. No intermediate string or <see cref="JsonDocument"/> is allocated. |
124 | 141 | /// </summary> |
125 | 142 | let tryCoerceValue (jsonOptions : JsonSerializerOptions voption) (targetType : Type) (value : objnull) : obj voption = |
126 | 143 | if isNull value then |
127 | 144 | ValueNone |
128 | 145 | elif targetType.IsInstanceOfType value then |
129 | 146 | ValueSome value |
130 | 147 | else |
131 | | - match toJsonString value with |
132 | | - | ValueNone -> ValueNone |
133 | | - | ValueSome json -> |
| 148 | + let buffer = ArrayBufferWriter<byte> 64 |
| 149 | + use writer = new Utf8JsonWriter (buffer) |
| 150 | + if not (writeJsonValue value writer) then |
| 151 | + ValueNone |
| 152 | + else |
| 153 | + writer.Flush () |
134 | 154 | try |
135 | 155 | let opts = jsonOptions |> ValueOption.defaultValue JsonSerializerOptions.Default |
136 | | - use doc = JsonDocument.Parse json |
137 | | - doc.Deserialize (targetType, opts) |> ValueSome |
| 156 | + JsonSerializer.Deserialize (buffer.WrittenSpan, targetType, opts) |> ValueSome |
138 | 157 | with _ -> |
139 | 158 | ValueNone |
140 | 159 |
|
|
0 commit comments