Omit upcasts where possible (#178)

This commit is contained in:
Patrick Stevens
2024-07-01 17:45:36 +01:00
committed by GitHub
parent c590db2a65
commit 434c042510
4 changed files with 126 additions and 11 deletions

View File

@@ -236,6 +236,55 @@ module FirstDuJsonSerializeExtension =
node.Add ("data", dataNode) node.Add ("data", dataNode)
node :> _ node :> _
namespace ConsumePlugin
open System
open System.Collections.Generic
open System.Text.Json.Serialization
/// Module containing JSON serializing extension members for the HeaderAndValue type
[<AutoOpen>]
module HeaderAndValueJsonSerializeExtension =
/// Extension methods for JSON parsing
type HeaderAndValue with
/// Serialize to a JSON node
static member toJsonNode (input : HeaderAndValue) : System.Text.Json.Nodes.JsonNode =
let node = System.Text.Json.Nodes.JsonObject ()
do
node.Add ("header", (input.Header |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ("value", (input.Value |> System.Text.Json.Nodes.JsonValue.Create<string>))
node :> _
namespace ConsumePlugin
open System
open System.Collections.Generic
open System.Text.Json.Serialization
/// Module containing JSON serializing extension members for the Foo type
[<AutoOpen>]
module FooJsonSerializeExtension =
/// Extension methods for JSON parsing
type Foo with
/// Serialize to a JSON node
static member toJsonNode (input : Foo) : System.Text.Json.Nodes.JsonNode =
let node = System.Text.Json.Nodes.JsonObject ()
do
node.Add (
"message",
(input.Message
|> (fun field ->
match field with
| None -> null :> System.Text.Json.Nodes.JsonNode
| Some field -> HeaderAndValue.toJsonNode field
))
)
node :> _
namespace ConsumePlugin namespace ConsumePlugin
@@ -717,3 +766,41 @@ module FirstDuJsonParseExtension =
.GetValue<System.Int32> () .GetValue<System.Int32> ()
) )
| v -> failwith ("Unrecognised 'type' field value: " + v) | v -> failwith ("Unrecognised 'type' field value: " + v)
namespace ConsumePlugin
/// Module containing JSON parsing extension members for the HeaderAndValue type
[<AutoOpen>]
module HeaderAndValueJsonParseExtension =
/// Extension methods for JSON parsing
type HeaderAndValue with
/// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : HeaderAndValue =
let arg_1 =
(match node.["value"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("value")
)
)
| v -> v)
.AsValue()
.GetValue<System.String> ()
let arg_0 =
(match node.["header"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("header")
)
)
| v -> v)
.AsValue()
.GetValue<System.String> ()
{
Header = arg_0
Value = arg_1
}

View File

@@ -57,3 +57,17 @@ type FirstDu =
| EmptyCase | EmptyCase
| Case1 of data : string | Case1 of data : string
| Case2 of record : JsonRecordTypeWithBoth * i : int | Case2 of record : JsonRecordTypeWithBoth * i : int
[<WoofWare.Myriad.Plugins.JsonParse true>]
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
type HeaderAndValue =
{
Header : string
Value : string
}
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
type Foo =
{
Message : HeaderAndValue option
}

View File

@@ -449,7 +449,7 @@ module internal HttpClientGenerator =
SynExpr.createNew SynExpr.createNew
(SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "StringContent" ]) (SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "StringContent" ])
(SynExpr.createIdent' bodyParamName (SynExpr.createIdent' bodyParamName
|> SynExpr.pipeThroughFunction (JsonSerializeGenerator.serializeNode ty) |> SynExpr.pipeThroughFunction (fst (JsonSerializeGenerator.serializeNode ty))
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.createLambda SynExpr.createLambda
"node" "node"

View File

@@ -23,7 +23,8 @@ module internal JsonSerializeGenerator =
/// Given `input.Ident`, for example, choose how to add it to the ambient `node`. /// Given `input.Ident`, for example, choose how to add it to the ambient `node`.
/// The result is a line like `(fun ident -> InnerType.toJsonNode ident)` or `(fun ident -> JsonValue.Create ident)`. /// The result is a line like `(fun ident -> InnerType.toJsonNode ident)` or `(fun ident -> JsonValue.Create ident)`.
let rec serializeNode (fieldType : SynType) : SynExpr = /// Returns also a bool which is true if the resulting SynExpr represents something of type JsonNode.
let rec serializeNode (fieldType : SynType) : SynExpr * bool =
// TODO: serialization format for DateTime etc // TODO: serialization format for DateTime etc
match fieldType with match fieldType with
| DateOnly | DateOnly
@@ -36,20 +37,30 @@ module internal JsonSerializeGenerator =
// JsonValue.Create<type> // JsonValue.Create<type>
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ] SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
|> SynExpr.typeApp [ fieldType ] |> SynExpr.typeApp [ fieldType ]
|> fun e -> e, false
| NullableType ty -> | NullableType ty ->
// fun field -> if field.HasValue then {serializeNode ty} field.Value else JsonValue.Create null // fun field -> if field.HasValue then {serializeNode ty} field.Value else JsonValue.Create null
SynExpr.applyFunction (serializeNode ty) (SynExpr.createLongIdent [ "field" ; "Value" ]) let inner, innerIsJsonNode = serializeNode ty
SynExpr.applyFunction inner (SynExpr.createLongIdent [ "field" ; "Value" ])
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]) |> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
|> SynExpr.ifThenElse (SynExpr.createLongIdent [ "field" ; "HasValue" ]) (jsonNull ()) |> SynExpr.ifThenElse (SynExpr.createLongIdent [ "field" ; "HasValue" ]) (jsonNull ())
|> SynExpr.createLambda "field" |> SynExpr.createLambda "field"
|> fun e -> e, innerIsJsonNode
| OptionType ty -> | OptionType ty ->
// fun field -> match field with | None -> JsonValue.Create null | Some v -> {serializeNode ty} field // fun field -> match field with | None -> JsonValue.Create null | Some v -> {serializeNode ty} field
let noneClause = jsonNull () |> SynMatchClause.create (SynPat.named "None") let noneClause = jsonNull () |> SynMatchClause.create (SynPat.named "None")
let someClause = let someClause =
SynExpr.applyFunction (serializeNode ty) (SynExpr.createIdent "field") let inner, innerIsJsonNode = serializeNode ty
|> SynExpr.paren let target = SynExpr.applyFunction inner (SynExpr.createIdent "field")
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
if innerIsJsonNode then
target
else
target
|> SynExpr.paren
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
|> SynMatchClause.create ( |> SynMatchClause.create (
SynPat.identWithArgs [ Ident.create "Some" ] (SynArgPats.create [ Ident.create "field" ]) SynPat.identWithArgs [ Ident.create "Some" ] (SynArgPats.create [ Ident.create "field" ])
) )
@@ -57,6 +68,7 @@ module internal JsonSerializeGenerator =
[ noneClause ; someClause ] [ noneClause ; someClause ]
|> SynExpr.createMatch (SynExpr.createIdent "field") |> SynExpr.createMatch (SynExpr.createIdent "field")
|> SynExpr.createLambda "field" |> SynExpr.createLambda "field"
|> fun e -> e, true
| ArrayType ty | ArrayType ty
| ListType ty -> | ListType ty ->
// fun field -> // fun field ->
@@ -73,7 +85,7 @@ module internal JsonSerializeGenerator =
SynExpr.createIdent "field", SynExpr.createIdent "field",
SynExpr.applyFunction SynExpr.applyFunction
(SynExpr.createLongIdent [ "arr" ; "Add" ]) (SynExpr.createLongIdent [ "arr" ; "Add" ])
(SynExpr.paren (SynExpr.applyFunction (serializeNode ty) (SynExpr.createIdent "mem"))), (SynExpr.paren (SynExpr.applyFunction (fst (serializeNode ty)) (SynExpr.createIdent "mem"))),
range0 range0
) )
SynExpr.createIdent "arr" SynExpr.createIdent "arr"
@@ -86,6 +98,7 @@ module internal JsonSerializeGenerator =
|> SynBinding.basic [ Ident.create "arr" ] [] |> SynBinding.basic [ Ident.create "arr" ] []
] ]
|> SynExpr.createLambda "field" |> SynExpr.createLambda "field"
|> fun e -> e, false
| IDictionaryType (_keyType, valueType) | IDictionaryType (_keyType, valueType)
| DictionaryType (_keyType, valueType) | DictionaryType (_keyType, valueType)
| IReadOnlyDictionaryType (_keyType, valueType) | IReadOnlyDictionaryType (_keyType, valueType)
@@ -113,7 +126,7 @@ module internal JsonSerializeGenerator =
[ [
SynExpr.createLongIdent [ "key" ; "ToString" ] SynExpr.createLongIdent [ "key" ; "ToString" ]
|> SynExpr.applyTo (SynExpr.CreateConst ()) |> SynExpr.applyTo (SynExpr.CreateConst ())
SynExpr.applyFunction (serializeNode valueType) (SynExpr.createIdent "value") SynExpr.applyFunction (fst (serializeNode valueType)) (SynExpr.createIdent "value")
]), ]),
range0 range0
) )
@@ -127,6 +140,7 @@ module internal JsonSerializeGenerator =
|> SynBinding.basic [ Ident.create "ret" ] [] |> SynBinding.basic [ Ident.create "ret" ] []
] ]
|> SynExpr.createLambda "field" |> SynExpr.createLambda "field"
|> fun e -> e, false
| _ -> | _ ->
// {type}.toJsonNode // {type}.toJsonNode
let typeName = let typeName =
@@ -134,7 +148,7 @@ module internal JsonSerializeGenerator =
| SynType.LongIdent ident -> ident.LongIdent | SynType.LongIdent ident -> ident.LongIdent
| _ -> failwith $"Unrecognised type: %+A{fieldType}" | _ -> failwith $"Unrecognised type: %+A{fieldType}"
SynExpr.createLongIdent' (typeName @ [ Ident.create "toJsonNode" ]) SynExpr.createLongIdent' (typeName @ [ Ident.create "toJsonNode" ]), true
/// propertyName is probably a string literal, but it could be a [<Literal>] variable /// propertyName is probably a string literal, but it could be a [<Literal>] variable
/// `node.Add ({propertyName}, {toJsonNode})` /// `node.Add ({propertyName}, {toJsonNode})`
@@ -142,7 +156,7 @@ module internal JsonSerializeGenerator =
[ [
propertyName propertyName
SynExpr.pipeThroughFunction SynExpr.pipeThroughFunction
(serializeNode fieldType) (fst (serializeNode fieldType))
(SynExpr.createLongIdent' [ Ident.create "input" ; fieldId ]) (SynExpr.createLongIdent' [ Ident.create "input" ; fieldId ])
|> SynExpr.paren |> SynExpr.paren
] ]
@@ -286,7 +300,7 @@ module internal JsonSerializeGenerator =
let propertyName = getPropertyName (Option.get fieldData.Ident) fieldData.Attrs let propertyName = getPropertyName (Option.get fieldData.Ident) fieldData.Attrs
let node = let node =
SynExpr.applyFunction (serializeNode fieldData.Type) (SynExpr.createIdent' caseName) SynExpr.applyFunction (fst (serializeNode fieldData.Type)) (SynExpr.createIdent' caseName)
[ propertyName ; node ] [ propertyName ; node ]
|> SynExpr.tuple |> SynExpr.tuple