|
1 | 1 | namespace System.Text.Json.Serialization |
2 | 2 |
|
3 | 3 | open System |
| 4 | +open System.Runtime.InteropServices |
4 | 5 | open System.Text.Json |
5 | 6 | open System.Text.Json.Serialization.Helpers |
6 | 7 | open FSharp.Reflection |
7 | 8 |
|
8 | | -type internal TupleProperty = { Type: Type; NeedsNullChecking: bool } |
| 9 | +type internal TupleProperty<'T> = |
| 10 | + { Type: Type |
| 11 | + NeedsNullChecking: bool } |
9 | 12 |
|
10 | | -type JsonTupleConverter<'T> internal (fsOptions) = |
| 13 | + static member MakeUntyped fsOptions t = |
| 14 | + let needsNullChecking = |
| 15 | + let tIsNullable = isNullableFieldType fsOptions t |
| 16 | + not tIsNullable && not t.IsValueType |
| 17 | + { Type = t; NeedsNullChecking = needsNullChecking } |
| 18 | + |
| 19 | + static member MakeTyped fsOptions = |
| 20 | + TupleProperty<'T>.MakeUntyped fsOptions typeof<'T> |
| 21 | + |
| 22 | + member this.CheckNull(x: 'T) = |
| 23 | + if this.NeedsNullChecking && isNull (box x) then |
| 24 | + failf "Unexpected null inside tuple-array. Expected type %s, but got null." this.Type.Name |
| 25 | + |
| 26 | + member this.ReadAndDeserializeTyped(reader: Utf8JsonReader byref, options: JsonSerializerOptions) = |
| 27 | + reader.Read() |> ignore |
| 28 | + let res = JsonSerializer.Deserialize<'T>(&reader, options) |
| 29 | + this.CheckNull(res) |
| 30 | + res |
| 31 | + |
| 32 | +[<AbstractClass>] |
| 33 | +type BaseJsonTupleConverter<'T>() = |
11 | 34 | inherit JsonConverter<'T>() |
12 | 35 |
|
| 36 | + abstract ReadCore: Utf8JsonReader byref * JsonSerializerOptions -> 'T |
| 37 | + abstract WriteCore: Utf8JsonWriter * 'T * JsonSerializerOptions -> unit |
| 38 | + |
| 39 | + override this.Read(reader, typeToConvert, options) = |
| 40 | + expectAlreadyRead JsonTokenType.StartArray "array" &reader typeToConvert |
| 41 | + let res = this.ReadCore(&reader, options) |
| 42 | + readExpecting JsonTokenType.EndArray "end of array" &reader typeToConvert |
| 43 | + res |
| 44 | + |
| 45 | + override this.Write(writer, value, options) = |
| 46 | + writer.WriteStartArray() |
| 47 | + this.WriteCore(writer, value, options) |
| 48 | + writer.WriteEndArray() |
| 49 | + |
| 50 | + override _.HandleNull = true |
| 51 | + |
| 52 | +type JsonTupleConverter<'T>(fsOptions: JsonFSharpOptions) = |
| 53 | + inherit BaseJsonTupleConverter<'T>() |
| 54 | + |
13 | 55 | let ty = typeof<'T> |
14 | 56 | let fieldProps = |
15 | 57 | FSharpType.GetTupleElements(ty) |
16 | | - |> Array.map (fun t -> |
17 | | - let tIsNullable = isNullableFieldType fsOptions t |
18 | | - let needsNullChecking = not tIsNullable && not t.IsValueType |
19 | | - { Type = t; NeedsNullChecking = needsNullChecking } |
20 | | - ) |
| 58 | + |> Array.map (TupleProperty<obj>.MakeUntyped fsOptions.Record) |
21 | 59 | let ctor = FSharpValue.PreComputeTupleConstructor(ty) |
22 | 60 | let reader = FSharpValue.PreComputeTupleReader(ty) |
23 | 61 |
|
24 | | - override _.Read(reader, typeToConvert, options) = |
25 | | - expectAlreadyRead JsonTokenType.StartArray "array" &reader typeToConvert |
| 62 | + override _.ReadCore(reader, options) = |
26 | 63 | let elts = Array.zeroCreate fieldProps.Length |
27 | 64 | for i in 0 .. fieldProps.Length - 1 do |
28 | 65 | let p = fieldProps[i] |
29 | 66 | reader.Read() |> ignore |
30 | 67 | let value = JsonSerializer.Deserialize(&reader, p.Type, options) |
31 | | - if p.NeedsNullChecking && isNull (box value) then |
32 | | - failf "Unexpected null inside tuple-array. Expected type %s, but got null." p.Type.Name |
| 68 | + p.CheckNull value |
33 | 69 | elts[i] <- value |
34 | | - readExpecting JsonTokenType.EndArray "end of array" &reader typeToConvert |
35 | 70 | ctor elts :?> 'T |
36 | 71 |
|
37 | | - override _.Write(writer, value, options) = |
38 | | - writer.WriteStartArray() |
| 72 | + override _.WriteCore(writer, value, options) = |
39 | 73 | let values = reader value |
40 | 74 | for i in 0 .. fieldProps.Length - 1 do |
41 | 75 | JsonSerializer.Serialize(writer, values[i], fieldProps[i].Type, options) |
42 | | - writer.WriteEndArray() |
43 | 76 |
|
44 | | - override _.HandleNull = true |
| 77 | +type JsonTupleConverter<'T1, 'T2>(fsOptions: JsonFSharpOptions) = |
| 78 | + inherit BaseJsonTupleConverter<'T1 * 'T2>() |
| 79 | + |
| 80 | + let p1 = TupleProperty<'T1>.MakeTyped fsOptions.Record |
| 81 | + let p2 = TupleProperty<'T2>.MakeTyped fsOptions.Record |
| 82 | + |
| 83 | + override this.ReadCore(reader, options) = |
| 84 | + (p1.ReadAndDeserializeTyped(&reader, options), p2.ReadAndDeserializeTyped(&reader, options)) |
| 85 | + |
| 86 | + override this.WriteCore(writer, (x1, x2), options) = |
| 87 | + JsonSerializer.Serialize(writer, x1, options) |
| 88 | + JsonSerializer.Serialize(writer, x2, options) |
| 89 | + |
| 90 | +type JsonStructTupleConverter<'T1, 'T2>(fsOptions: JsonFSharpOptions) = |
| 91 | + inherit BaseJsonTupleConverter<struct ('T1 * 'T2)>() |
| 92 | + |
| 93 | + let p1 = TupleProperty<'T1>.MakeTyped fsOptions.Record |
| 94 | + let p2 = TupleProperty<'T2>.MakeTyped fsOptions.Record |
| 95 | + |
| 96 | + override this.ReadCore(reader, options) = |
| 97 | + (p1.ReadAndDeserializeTyped(&reader, options), p2.ReadAndDeserializeTyped(&reader, options)) |
| 98 | + |
| 99 | + override this.WriteCore(writer, (x1, x2), options) = |
| 100 | + JsonSerializer.Serialize(writer, x1, options) |
| 101 | + JsonSerializer.Serialize(writer, x2, options) |
| 102 | + |
| 103 | +type JsonTupleConverter<'T1, 'T2, 'T3>(fsOptions: JsonFSharpOptions) = |
| 104 | + inherit BaseJsonTupleConverter<'T1 * 'T2 * 'T3>() |
| 105 | + |
| 106 | + let p1 = TupleProperty<'T1>.MakeTyped fsOptions.Record |
| 107 | + let p2 = TupleProperty<'T2>.MakeTyped fsOptions.Record |
| 108 | + let p3 = TupleProperty<'T3>.MakeTyped fsOptions.Record |
| 109 | + |
| 110 | + override this.ReadCore(reader, options) = |
| 111 | + (p1.ReadAndDeserializeTyped(&reader, options), |
| 112 | + p2.ReadAndDeserializeTyped(&reader, options), |
| 113 | + p3.ReadAndDeserializeTyped(&reader, options)) |
| 114 | + |
| 115 | + override this.WriteCore(writer, (x1, x2, x3), options) = |
| 116 | + JsonSerializer.Serialize(writer, x1, options) |
| 117 | + JsonSerializer.Serialize(writer, x2, options) |
| 118 | + JsonSerializer.Serialize(writer, x3, options) |
| 119 | + |
| 120 | +type JsonStructTupleConverter<'T1, 'T2, 'T3>(fsOptions: JsonFSharpOptions) = |
| 121 | + inherit BaseJsonTupleConverter<struct ('T1 * 'T2 * 'T3)>() |
| 122 | + |
| 123 | + let p1 = TupleProperty<'T1>.MakeTyped fsOptions.Record |
| 124 | + let p2 = TupleProperty<'T2>.MakeTyped fsOptions.Record |
| 125 | + let p3 = TupleProperty<'T3>.MakeTyped fsOptions.Record |
| 126 | + |
| 127 | + override this.ReadCore(reader, options) = |
| 128 | + (p1.ReadAndDeserializeTyped(&reader, options), |
| 129 | + p2.ReadAndDeserializeTyped(&reader, options), |
| 130 | + p3.ReadAndDeserializeTyped(&reader, options)) |
| 131 | + |
| 132 | + override this.WriteCore(writer, (x1, x2, x3), options) = |
| 133 | + JsonSerializer.Serialize(writer, x1, options) |
| 134 | + JsonSerializer.Serialize(writer, x2, options) |
| 135 | + JsonSerializer.Serialize(writer, x3, options) |
| 136 | + |
| 137 | +type JsonTupleConverter<'T1, 'T2, 'T3, 'T4>(fsOptions: JsonFSharpOptions) = |
| 138 | + inherit BaseJsonTupleConverter<'T1 * 'T2 * 'T3 * 'T4>() |
| 139 | + |
| 140 | + let p1 = TupleProperty<'T1>.MakeTyped fsOptions.Record |
| 141 | + let p2 = TupleProperty<'T2>.MakeTyped fsOptions.Record |
| 142 | + let p3 = TupleProperty<'T3>.MakeTyped fsOptions.Record |
| 143 | + let p4 = TupleProperty<'T4>.MakeTyped fsOptions.Record |
| 144 | + |
| 145 | + override this.ReadCore(reader, options) = |
| 146 | + (p1.ReadAndDeserializeTyped(&reader, options), |
| 147 | + p2.ReadAndDeserializeTyped(&reader, options), |
| 148 | + p3.ReadAndDeserializeTyped(&reader, options), |
| 149 | + p4.ReadAndDeserializeTyped(&reader, options)) |
| 150 | + |
| 151 | + override this.WriteCore(writer, (x1, x2, x3, x4), options) = |
| 152 | + JsonSerializer.Serialize(writer, x1, options) |
| 153 | + JsonSerializer.Serialize(writer, x2, options) |
| 154 | + JsonSerializer.Serialize(writer, x3, options) |
| 155 | + JsonSerializer.Serialize(writer, x4, options) |
| 156 | + |
| 157 | +type JsonStructTupleConverter<'T1, 'T2, 'T3, 'T4>(fsOptions: JsonFSharpOptions) = |
| 158 | + inherit BaseJsonTupleConverter<struct ('T1 * 'T2 * 'T3 * 'T4)>() |
| 159 | + |
| 160 | + let p1 = TupleProperty<'T1>.MakeTyped fsOptions.Record |
| 161 | + let p2 = TupleProperty<'T2>.MakeTyped fsOptions.Record |
| 162 | + let p3 = TupleProperty<'T3>.MakeTyped fsOptions.Record |
| 163 | + let p4 = TupleProperty<'T4>.MakeTyped fsOptions.Record |
| 164 | + |
| 165 | + override this.ReadCore(reader, options) = |
| 166 | + (p1.ReadAndDeserializeTyped(&reader, options), |
| 167 | + p2.ReadAndDeserializeTyped(&reader, options), |
| 168 | + p3.ReadAndDeserializeTyped(&reader, options), |
| 169 | + p4.ReadAndDeserializeTyped(&reader, options)) |
| 170 | + |
| 171 | + override this.WriteCore(writer, (x1, x2, x3, x4), options) = |
| 172 | + JsonSerializer.Serialize(writer, x1, options) |
| 173 | + JsonSerializer.Serialize(writer, x2, options) |
| 174 | + JsonSerializer.Serialize(writer, x3, options) |
| 175 | + JsonSerializer.Serialize(writer, x4, options) |
45 | 176 |
|
46 | | - new(fsOptions: JsonFSharpOptions) = JsonTupleConverter<'T>(fsOptions.Record) |
47 | 177 |
|
48 | | -type JsonTupleConverter(fsOptions) = |
| 178 | +type JsonTupleConverter(fsOptions, [<Optional>] forceGeneric) = |
49 | 179 | inherit JsonConverterFactory() |
50 | 180 |
|
51 | 181 | static member internal CanConvert(typeToConvert: Type) = |
52 | 182 | TypeCache.isTuple typeToConvert |
53 | 183 |
|
54 | | - static member internal CreateConverter(typeToConvert: Type, fsOptions: JsonFSharpOptions) = |
55 | | - typedefof<JsonTupleConverter<_>> |
56 | | - .MakeGenericType([| typeToConvert |]) |
57 | | - .GetConstructor([| typeof<JsonFSharpOptions> |]) |
58 | | - .Invoke([| fsOptions |]) |
59 | | - :?> JsonConverter |
| 184 | + static member internal CreateConverter |
| 185 | + (typeToConvert: Type, fsOptions: JsonFSharpOptions, [<Optional>] forceGeneric: bool) |
| 186 | + = |
| 187 | + let converterType = |
| 188 | + if forceGeneric then |
| 189 | + typedefof<JsonTupleConverter<_>>.MakeGenericType(typeToConvert) |
| 190 | + else |
| 191 | + let targs = typeToConvert.GetGenericArguments() |
| 192 | + let isStruct = |
| 193 | + typeToConvert.GetGenericTypeDefinition().Name.StartsWith("ValueTuple") |
| 194 | + match targs.Length, isStruct with |
| 195 | + | 2, false -> typedefof<JsonTupleConverter<_, _>>.MakeGenericType(targs) |
| 196 | + | 3, false -> typedefof<JsonTupleConverter<_, _, _>>.MakeGenericType(targs) |
| 197 | + | 4, false -> typedefof<JsonTupleConverter<_, _, _, _>>.MakeGenericType(targs) |
| 198 | + | 2, true -> typedefof<JsonStructTupleConverter<_, _>>.MakeGenericType(targs) |
| 199 | + | 3, true -> typedefof<JsonStructTupleConverter<_, _, _>>.MakeGenericType(targs) |
| 200 | + | 4, true -> typedefof<JsonStructTupleConverter<_, _, _, _>>.MakeGenericType(targs) |
| 201 | + | _ -> typedefof<JsonTupleConverter<_>>.MakeGenericType(typeToConvert) |
| 202 | + converterType.GetConstructor([| typeof<JsonFSharpOptions> |]).Invoke([| fsOptions |]) :?> JsonConverter |
60 | 203 |
|
61 | 204 | override _.CanConvert(typeToConvert) = |
62 | 205 | JsonTupleConverter.CanConvert(typeToConvert) |
63 | 206 |
|
64 | 207 | override _.CreateConverter(typeToConvert, _options) = |
65 | | - JsonTupleConverter.CreateConverter(typeToConvert, fsOptions) |
| 208 | + JsonTupleConverter.CreateConverter(typeToConvert, fsOptions, forceGeneric) |
0 commit comments