Skip to content

Commit b5bc81f

Browse files
committed
Fix #97: implement notification interfaces
1 parent cff6551 commit b5bc81f

File tree

2 files changed

+79
-57
lines changed

2 files changed

+79
-57
lines changed

src/FSharp.SystemTextJson/Record.fs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ type internal IRecordConverter =
2424
type JsonRecordConverter<'T>(options: JsonSerializerOptions, fsOptions: JsonFSharpOptions) =
2525
inherit JsonConverter<'T>()
2626

27-
let recordType: Type = typeof<'T>
27+
static let recordType: Type = typeof<'T>
28+
29+
static let hasOnSerializing = recordType.IsAssignableFrom(typeof<IJsonOnSerializing>)
30+
static let hasOnSerialized = recordType.IsAssignableFrom(typeof<IJsonOnSerialized>)
31+
static let hasOnDeserializing = recordType.IsAssignableFrom(typeof<IJsonOnDeserializing>)
32+
static let hasOnDeserialized = recordType.IsAssignableFrom(typeof<IJsonOnDeserialized>)
2833

2934
let fieldProps =
3035
FSharpType.GetRecordFields(recordType, true)
@@ -140,13 +145,17 @@ type JsonRecordConverter<'T>(options: JsonSerializerOptions, fsOptions: JsonFSha
140145
if isNull fields.[i] && fieldProps.[i].MustBePresent then
141146
raise (JsonException("Missing field for record type " + recordType.FullName + ": " + fieldProps.[i].Name))
142147

143-
ctor fields :?> 'T
148+
let res = ctor fields :?> 'T
149+
if hasOnDeserializing then (unbox<IJsonOnDeserializing> res).OnDeserializing()
150+
if hasOnDeserialized then (unbox<IJsonOnDeserialized> res).OnDeserialized()
151+
res
144152

145153
override this.Write(writer, value, options) =
146154
writer.WriteStartObject()
147155
this.WriteRestOfObject(writer, value, options)
148156

149157
member internal _.WriteRestOfObject(writer, value, options) =
158+
if hasOnSerializing then (unbox<IJsonOnSerializing> value).OnSerializing()
150159
let values = dector value
151160
for i in 0..fieldProps.Length-1 do
152161
let v = values.[i]
@@ -155,6 +164,7 @@ type JsonRecordConverter<'T>(options: JsonSerializerOptions, fsOptions: JsonFSha
155164
writer.WritePropertyName(p.Name)
156165
JsonSerializer.Serialize(writer, v, p.Type, options)
157166
writer.WriteEndObject()
167+
if hasOnSerialized then (unbox<IJsonOnSerialized> value).OnSerialized()
158168

159169
interface IRecordConverter with
160170
member this.ReadRestOfObject(reader, options, skipFirstRead) =

src/FSharp.SystemTextJson/Union.fs

Lines changed: 67 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@ type JsonUnionConverter<'T>
4545
let namedFields = fsOptions.UnionEncoding.HasFlag JsonUnionEncoding.NamedFields
4646
let unwrapFieldlessTags = fsOptions.UnionEncoding.HasFlag JsonUnionEncoding.UnwrapFieldlessTags
4747

48-
let ty = typeof<'T>
48+
static let unionType = typeof<'T>
49+
50+
static let hasOnSerializing = unionType.IsAssignableFrom(typeof<IJsonOnSerializing>)
51+
static let hasOnSerialized = unionType.IsAssignableFrom(typeof<IJsonOnSerialized>)
52+
static let hasOnDeserializing = unionType.IsAssignableFrom(typeof<IJsonOnDeserializing>)
53+
static let hasOnDeserialized = unionType.IsAssignableFrom(typeof<IJsonOnDeserialized>)
4954

5055
let cases =
5156
cases
@@ -115,7 +120,7 @@ type JsonUnionConverter<'T>
115120
MinExpectedFieldCount = fields |> Seq.filter (fun f -> f.MustBePresent) |> Seq.length
116121
})
117122

118-
let tagReader = FSharpValue.PreComputeUnionTagReader(ty, true)
123+
let tagReader = FSharpValue.PreComputeUnionTagReader(unionType, true)
119124

120125
let hasDistinctFieldNames, fieldlessCase, allFields =
121126
let mutable fieldlessCase = ValueNone
@@ -184,7 +189,7 @@ type JsonUnionConverter<'T>
184189
| false, _ -> ValueNone
185190
match found with
186191
| ValueNone ->
187-
raise (JsonException("Unknown case for union type " + ty.FullName + ": " + reader.GetString()))
192+
raise (JsonException("Unknown case for union type " + unionType.FullName + ": " + reader.GetString()))
188193
| ValueSome case ->
189194
case
190195

@@ -207,7 +212,7 @@ type JsonUnionConverter<'T>
207212
| false, _ -> ValueNone
208213
match found with
209214
| ValueNone ->
210-
raise (JsonException("Unknown case for union type " + ty.FullName + ": " + tag))
215+
raise (JsonException("Unknown case for union type " + unionType.FullName + ": " + tag))
211216
| ValueSome case ->
212217
case
213218

@@ -230,7 +235,7 @@ type JsonUnionConverter<'T>
230235
| false, _ -> ValueNone
231236
match found with
232237
| ValueNone ->
233-
raise (JsonException("Unknown case for union type " + ty.FullName + " due to unknown field: " + reader.GetString()))
238+
raise (JsonException("Unknown case for union type " + unionType.FullName + " due to unknown field: " + reader.GetString()))
234239
| ValueSome case ->
235240
case
236241

@@ -254,7 +259,7 @@ type JsonUnionConverter<'T>
254259
let readField (reader: byref<Utf8JsonReader>) (case: Case) (f: Field) (options: JsonSerializerOptions) =
255260
reader.Read() |> ignore
256261
if f.MustBeNonNull && reader.TokenType = JsonTokenType.Null then
257-
let msg = sprintf "%s.%s(%s) was expected to be of type %s, but was null." ty.Name case.Name f.Name f.Type.Name
262+
let msg = sprintf "%s.%s(%s) was expected to be of type %s, but was null." unionType.Name case.Name f.Name f.Type.Name
258263
raise (JsonException msg)
259264
else
260265
JsonSerializer.Deserialize(&reader, f.Type, options)
@@ -264,11 +269,11 @@ type JsonUnionConverter<'T>
264269
let fields = Array.copy case.DefaultFields
265270
for i in 0..fieldCount-1 do
266271
fields.[i] <- readField &reader case case.Fields.[i] options
267-
readExpecting JsonTokenType.EndArray "end of array" &reader ty
272+
readExpecting JsonTokenType.EndArray "end of array" &reader unionType
268273
case.Ctor fields :?> 'T
269274

270275
let readFieldsAsArray (reader: byref<Utf8JsonReader>) (case: Case) (options: JsonSerializerOptions) =
271-
readExpecting JsonTokenType.StartArray "array" &reader ty
276+
readExpecting JsonTokenType.StartArray "array" &reader unionType
272277
readFieldsAsRestOfArray &reader case options
273278

274279
let coreReadFieldsAsRestOfObject (reader: byref<Utf8JsonReader>) (case: Case) (skipFirstRead: bool) (options: JsonSerializerOptions) =
@@ -292,7 +297,7 @@ type JsonUnionConverter<'T>
292297
| _ -> ()
293298

294299
if fieldsFound < case.MinExpectedFieldCount && not options.IgnoreNullValues then
295-
raise (JsonException("Missing field for union type " + ty.FullName))
300+
raise (JsonException("Missing field for union type " + unionType.FullName))
296301
case.Ctor fields :?> 'T
297302

298303
let readFieldsAsRestOfObject (reader: byref<Utf8JsonReader>) (case: Case) (skipFirstRead: bool) (options: JsonSerializerOptions) =
@@ -304,7 +309,7 @@ type JsonUnionConverter<'T>
304309
coreReadFieldsAsRestOfObject &reader case skipFirstRead options
305310

306311
let readFieldsAsObject (reader: byref<Utf8JsonReader>) (case: Case) (options: JsonSerializerOptions) =
307-
readExpecting JsonTokenType.StartObject "object" &reader ty
312+
readExpecting JsonTokenType.StartObject "object" &reader unionType
308313
readFieldsAsRestOfObject &reader case false options
309314

310315
let readFields (reader: byref<Utf8JsonReader>) case options =
@@ -327,60 +332,60 @@ type JsonUnionConverter<'T>
327332
match document.RootElement.TryGetProperty fsOptions.UnionTagName with
328333
| true, element -> getCaseByTagString (element.GetString())
329334
| false, _ ->
330-
sprintf "Failed to find union case field for %s: expected %s" ty.FullName fsOptions.UnionTagName
335+
sprintf "Failed to find union case field for %s: expected %s" unionType.FullName fsOptions.UnionTagName
331336
|> JsonException
332337
|> raise
333338

334339
let getCase (reader: byref<Utf8JsonReader>) =
335340
let mutable snapshot = reader
336-
if readIsExpectingPropertyNamed fsOptions.UnionTagName &snapshot ty then
337-
readExpectingPropertyNamed fsOptions.UnionTagName &reader ty
338-
readExpecting JsonTokenType.String "case name" &reader ty
341+
if readIsExpectingPropertyNamed fsOptions.UnionTagName &snapshot unionType then
342+
readExpectingPropertyNamed fsOptions.UnionTagName &reader unionType
343+
readExpecting JsonTokenType.String "case name" &reader unionType
339344
struct (getCaseByTagReader &reader, false)
340345
elif fsOptions.UnionEncoding.HasFlag JsonUnionEncoding.AllowUnorderedTag then
341346
struct (getCaseFromDocument reader, true)
342347
else
343-
sprintf "Failed to find union case field for %s: expected %s" ty.FullName fsOptions.UnionTagName
348+
sprintf "Failed to find union case field for %s: expected %s" unionType.FullName fsOptions.UnionTagName
344349
|> JsonException
345350
|> raise
346351

347352
let readAdjacentTag (reader: byref<Utf8JsonReader>) (options: JsonSerializerOptions) =
348-
expectAlreadyRead JsonTokenType.StartObject "object" &reader ty
353+
expectAlreadyRead JsonTokenType.StartObject "object" &reader unionType
349354
let struct (case, usedDocument) = getCase &reader
350355
let res =
351356
if case.Fields.Length > 0 then
352-
readExpectingPropertyNamed fsOptions.UnionFieldsName &reader ty
357+
readExpectingPropertyNamed fsOptions.UnionFieldsName &reader unionType
353358
readFields &reader case options
354359
else
355360
case.Ctor [||] :?> 'T
356361
if usedDocument then
357362
reader.Read() |> ignore
358363
reader.Skip()
359-
readExpecting JsonTokenType.EndObject "end of object" &reader ty
364+
readExpecting JsonTokenType.EndObject "end of object" &reader unionType
360365
res
361366

362367
let readExternalTag (reader: byref<Utf8JsonReader>) (options: JsonSerializerOptions) =
363-
expectAlreadyRead JsonTokenType.StartObject "object" &reader ty
364-
readExpecting JsonTokenType.PropertyName "case name" &reader ty
368+
expectAlreadyRead JsonTokenType.StartObject "object" &reader unionType
369+
readExpecting JsonTokenType.PropertyName "case name" &reader unionType
365370
let case = getCaseByTagReader &reader
366371
let res = readFields &reader case options
367-
readExpecting JsonTokenType.EndObject "end of object" &reader ty
372+
readExpecting JsonTokenType.EndObject "end of object" &reader unionType
368373
res
369374

370375
let readInternalTag (reader: byref<Utf8JsonReader>) (options: JsonSerializerOptions) =
371376
if namedFields then
372-
expectAlreadyRead JsonTokenType.StartObject "object" &reader ty
377+
expectAlreadyRead JsonTokenType.StartObject "object" &reader unionType
373378
let mutable snapshot = reader
374379
let struct (case, _usedDocument) = getCase &snapshot
375380
readFieldsAsRestOfObject &reader case false options
376381
else
377-
expectAlreadyRead JsonTokenType.StartArray "array" &reader ty
378-
readExpecting JsonTokenType.String "case name" &reader ty
382+
expectAlreadyRead JsonTokenType.StartArray "array" &reader unionType
383+
readExpecting JsonTokenType.String "case name" &reader unionType
379384
let case = getCaseByTagReader &reader
380385
readFieldsAsRestOfArray &reader case options
381386

382387
let readUntagged (reader: byref<Utf8JsonReader>) (options: JsonSerializerOptions) =
383-
expectAlreadyRead JsonTokenType.StartObject "object" &reader ty
388+
expectAlreadyRead JsonTokenType.StartObject "object" &reader unionType
384389
reader.Read() |> ignore
385390
match reader.TokenType with
386391
| JsonTokenType.PropertyName ->
@@ -389,9 +394,9 @@ type JsonUnionConverter<'T>
389394
| JsonTokenType.EndObject ->
390395
match fieldlessCase with
391396
| ValueSome case -> case.Ctor [||] :?> 'T
392-
| ValueNone -> fail "case field" &reader ty
397+
| ValueNone -> fail "case field" &reader unionType
393398
| _ ->
394-
fail "case field" &reader ty
399+
fail "case field" &reader unionType
395400

396401
let writeFieldsAsRestOfArray (writer: Utf8JsonWriter) (case: Case) (value: obj) (options: JsonSerializerOptions) =
397402
let fields = case.Fields
@@ -462,38 +467,45 @@ type JsonUnionConverter<'T>
462467
writeFieldsAsObject writer case value options
463468

464469
override _.Read(reader, _typeToConvert, options) =
465-
match reader.TokenType with
466-
| JsonTokenType.Null when Helpers.isNullableUnion ty ->
467-
(null : obj) :?> 'T
468-
| JsonTokenType.String when unwrapFieldlessTags ->
469-
let case = getCaseByTagReader &reader
470-
case.Ctor [||] :?> 'T
471-
| _ ->
472-
match baseFormat with
473-
| JsonUnionEncoding.AdjacentTag -> readAdjacentTag &reader options
474-
| JsonUnionEncoding.ExternalTag -> readExternalTag &reader options
475-
| JsonUnionEncoding.InternalTag -> readInternalTag &reader options
476-
| UntaggedBit ->
477-
if not hasDistinctFieldNames then
478-
raise (JsonException(sprintf "Union %s can't be deserialized as Untagged because it has duplicate field names across unions" ty.FullName))
479-
readUntagged &reader options
480-
| _ -> raise (JsonException("Invalid union encoding: " + string fsOptions.UnionEncoding))
470+
let v =
471+
match reader.TokenType with
472+
| JsonTokenType.Null when Helpers.isNullableUnion unionType ->
473+
(null : obj) :?> 'T
474+
| JsonTokenType.String when unwrapFieldlessTags ->
475+
let case = getCaseByTagReader &reader
476+
case.Ctor [||] :?> 'T
477+
| _ ->
478+
match baseFormat with
479+
| JsonUnionEncoding.AdjacentTag -> readAdjacentTag &reader options
480+
| JsonUnionEncoding.ExternalTag -> readExternalTag &reader options
481+
| JsonUnionEncoding.InternalTag -> readInternalTag &reader options
482+
| UntaggedBit ->
483+
if not hasDistinctFieldNames then
484+
raise (JsonException(sprintf "Union %s can't be deserialized as Untagged because it has duplicate field names across unions" unionType.FullName))
485+
readUntagged &reader options
486+
| _ -> raise (JsonException("Invalid union encoding: " + string fsOptions.UnionEncoding))
487+
if hasOnDeserializing then (unbox<IJsonOnDeserializing> v).OnDeserializing()
488+
if hasOnDeserialized then (unbox<IJsonOnDeserialized> v).OnDeserialized()
489+
v
481490

482491
override _.Write(writer, value, options) =
492+
if hasOnSerializing then (unbox<IJsonOnSerializing> value).OnSerializing()
483493
let value = box value
484-
if isNull value then writer.WriteNullValue() else
485-
486-
let tag = tagReader value
487-
let case = cases.[tag]
488-
if unwrapFieldlessTags && case.Fields.Length = 0 then
489-
writer.WriteStringValue(case.Name)
494+
if isNull value then
495+
writer.WriteNullValue()
490496
else
491-
match baseFormat with
492-
| JsonUnionEncoding.AdjacentTag -> writeAdjacentTag writer case value options
493-
| JsonUnionEncoding.ExternalTag -> writeExternalTag writer case value options
494-
| JsonUnionEncoding.InternalTag -> writeInternalTag writer case value options
495-
| UntaggedBit -> writeUntagged writer case value options
496-
| _ -> raise (JsonException("Invalid union encoding: " + string fsOptions.UnionEncoding))
497+
let tag = tagReader value
498+
let case = cases.[tag]
499+
if unwrapFieldlessTags && case.Fields.Length = 0 then
500+
writer.WriteStringValue(case.Name)
501+
else
502+
match baseFormat with
503+
| JsonUnionEncoding.AdjacentTag -> writeAdjacentTag writer case value options
504+
| JsonUnionEncoding.ExternalTag -> writeExternalTag writer case value options
505+
| JsonUnionEncoding.InternalTag -> writeInternalTag writer case value options
506+
| UntaggedBit -> writeUntagged writer case value options
507+
| _ -> raise (JsonException("Invalid union encoding: " + string fsOptions.UnionEncoding))
508+
if hasOnSerialized then (unbox<IJsonOnSerialized> value).OnSerialized()
497509

498510
type JsonSkippableConverter<'T>() =
499511
inherit JsonConverter<Skippable<'T>>()

0 commit comments

Comments
 (0)