WoofWare.Whippet.Plugin.Json 0.1.6

dotnet add package WoofWare.Whippet.Plugin.Json --version 0.1.6                
NuGet\Install-Package WoofWare.Whippet.Plugin.Json -Version 0.1.6                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="WoofWare.Whippet.Plugin.Json" Version="0.1.6">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add WoofWare.Whippet.Plugin.Json --version 0.1.6                
#r "nuget: WoofWare.Whippet.Plugin.Json, 0.1.6"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install WoofWare.Whippet.Plugin.Json as a Cake Addin
#addin nuget:?package=WoofWare.Whippet.Plugin.Json&version=0.1.6

// Install WoofWare.Whippet.Plugin.Json as a Cake Tool
#tool nuget:?package=WoofWare.Whippet.Plugin.Json&version=0.1.6                

WoofWare.Whippet.Plugin.Json

This is a Whippet plugin defining JSON parse and serialise methods.

It is a copy of the corresponding Myriad JSON plugin in WoofWare.Myriad, taken from commit d59ebdfccb87a06579fb99008a15f58ea8be394e.

What's the point?

System.Text.Json, in a PublishAot context, relies on C# source generators. The default reflection-heavy implementations have the necessary code trimmed away, and result in a runtime exception. But C# source generators are entirely unsupported in F#.

These generators handle going from your strongly-typed domain objects to System.Text.Json.Nodes.JsonNode, and back.

Usage: JsonParse

Define a Dto.fs file like the following:

namespace MyNamespace

open WoofWare.Whippet.Plugin.Json

[<JsonParse>]
type InnerType =
    {
        [<JsonPropertyName "something">]
        Thing : string
    }

/// My whatnot
[<JsonParse (* isExtensionMethod = *) false>]
type JsonRecordType =
    {
        /// A thing!
        A : int
        /// Another thing!
        B : string
        [<System.Text.Json.Serialization.JsonPropertyName "hi">]
        C : int list
        D : InnerType
    }

In your fsproj:

<Project>
    <ItemGroup>
        <Compile Include="Dto.fs" />
        <Compile Include="GeneratedDto.fs">
            <WhippetFile>Dto.fs</WhippetFile>
        </Compile>
    </ItemGroup>

    <ItemGroup>
        
        <PackageReference Include="WoofWare.Whippet.Plugin.Json.Attributes" Version="" />
        
        <PackageReference Include="WoofWare.Whippet.Plugin.Json" WhippetPlugin="true" Version="" />
        <PackageReference Include="WoofWare.Whippet" Version="" PrivateAssets="all" />
    </ItemGroup>
</Project>

The generator will produce a file somewhat like the following:

/// Module containing JSON parsing methods for the InnerType type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module InnerType =
    /// Parse from a JSON node.
    let jsonParse (node: System.Text.Json.Nodes.JsonNode) : InnerType =
        let Thing = node.["something"].AsValue().GetValue<string>()
        { Thing = Thing }
namespace UsePlugin

/// Module containing JSON parsing methods for the JsonRecordType type
[<AutoOpen>]
module JsonRecordTypeExtension =
    type JsonRecordType with
        /// Parse from a JSON node.
        let jsonParse (node: System.Text.Json.Nodes.JsonNode) : JsonRecordType =
            let D = InnerType.jsonParse node.["d"]

            let C =
                node.["hi"].AsArray() |> Seq.map (fun elt -> elt.GetValue<int>()) |> List.ofSeq

            let B = node.["b"].AsValue().GetValue<string>()
            let A = node.["a"].AsValue().GetValue<int>()
            { A = A; B = B; C = C; D = D }

You may instead choose to define attributes with the correct name yourself (if you don't want to take a dependency on the WoofWare.Whippet.Plugin.Json.Attributes package). Alternatively, you may omit the attributes and the runtime dependency, and control the generator entirely through the fsproj file:

<Project>
    <ItemGroup>
        <Compile Include="Dto.fs" />
        <Compile Include="GeneratedDto.fs">
            <WhippetFile>Dto.fs</WhippetFile>
            <WhippetParamInnerType>JsonParse</WhippetParamInnerType>
            <WhippetParamJsonRecordType>JsonParse(false)</WhippetParamJsonRecordType>
        </Compile>
    </ItemGroup>

    <ItemGroup>
        
        <PackageReference Include="WoofWare.Whippet.Plugin.Json" WhippetPlugin="true" Version="" />
        <PackageReference Include="WoofWare.Whippet" Version="" PrivateAssets="all" />
    </ItemGroup>
</Project>

(This plugin follows a standard convention taken by WoofWare.Whippet.Plugin plugins, where you use Whippet parameters with the same name as each input type, whose contents are a !-delimited list of the generators which you wish to apply to that input type.)

Usage: JsonSerialize

Define a Dto.fs file like the following:

namespace MyNamespace

open WoofWare.Whippet.Plugin.Json

[<JsonSerialize true>]
type InnerTypeWithBoth =
    {
        [<JsonPropertyName("it's-a-me")>]
        Thing : string
        ReadOnlyDict : IReadOnlyDictionary<string, Uri list>
    }

In your fsproj:

<Project>
    <ItemGroup>
        <Compile Include="Dto.fs" />
        <Compile Include="GeneratedDto.fs">
            <WhippetFile>Dto.fs</WhippetFile>
        </Compile>
    </ItemGroup>

    <ItemGroup>
        
        <PackageReference Include="WoofWare.Whippet.Plugin.Json.Attributes" Version="" />
        
        <PackageReference Include="WoofWare.Whippet.Plugin.Json" WhippetPlugin="true" Version="" />
        <PackageReference Include="WoofWare.Whippet" Version="" PrivateAssets="all" />
    </ItemGroup>
</Project>

The generator will produce a file somewhat like the following:

namespace UsePlugin

/// Module containing JSON parsing methods for the JsonRecordType type
[<AutoOpen>]
module JsonRecordTypeExtension =
    type InnerTypeWithBoth with
        let toJsonNode (input : InnerTypeWithBoth) : System.Text.Json.Nodes.JsonNode =
            let node = System.Text.Json.Nodes.JsonObject ()

            do
                node.Add (("it's-a-me"), System.Text.Json.Nodes.JsonValue.Create<string> input.Thing)

                node.Add (
                    "ReadOnlyDict",
                    (fun field ->
                        let ret = System.Text.Json.Nodes.JsonObject ()

                        for (KeyValue (key, value)) in field do
                            ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<Uri> value)

                        ret
                    ) input.Map
                )

            node

You may instead choose to define attributes with the correct name yourself (if you don't want to take a dependency on the WoofWare.Whippet.Plugin.Json.Attributes package). Alternatively, you may omit the attributes and the runtime dependency, and control the generator entirely through the fsproj file:

<Project>
    <ItemGroup>
        <Compile Include="Dto.fs" />
        <Compile Include="GeneratedDto.fs">
            <WhippetFile>Dto.fs</WhippetFile>
            <WhippetParamInnerType>JsonSerialize</WhippetParamInnerType>
            <WhippetParamJsonRecordType>JsonSerialize(false)</WhippetParamJsonRecordType>
        </Compile>
    </ItemGroup>

    <ItemGroup>
        
        <PackageReference Include="WoofWare.Whippet.Plugin.Json" WhippetPlugin="true" Version="" />
        <PackageReference Include="WoofWare.Whippet" Version="" PrivateAssets="all" />
    </ItemGroup>
</Project>

(This plugin follows a standard convention taken by WoofWare.Whippet.Plugin plugins, where you use Whippet parameters with the same name as each input type, whose contents are a !-delimited list of the generators which you wish to apply to that input type.)

Notes

  • The plugin includes an opinionated de/serializer for discriminated unions. (Any such serializer must be opinionated, because JSON does not natively model DUs.)
  • Supply the optional boolean arg false to the [<JsonParse>]/[<JsonSerialize>] attributes, or pass it via <WhippetParamMyType>JsonParse(false)</WhippetParamMyType>, to get a genuine module that can be consumed from C#.
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on WoofWare.Whippet.Plugin.Json:

Package Downloads
WoofWare.Whippet.Plugin.HttpClient

Whippet F# source generator plugin, for generating RestEase-style HTTP clients.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.6 110 10/8/2024
0.1.5 105 10/8/2024
0.1.4 111 10/8/2024
0.1.3 142 10/7/2024
0.1.2 110 10/7/2024
0.1.1 94 10/7/2024