Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand documentation #100

Merged
merged 15 commits into from
Nov 6, 2021
Merged
34 changes: 33 additions & 1 deletion .github/workflows/dotnetcore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ jobs:

steps:
- uses: actions/checkout@v2
- name: Setup .NET Core
- name: Setup .NET Core 5
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.301
- name: Setup .NET Core 3
uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.405
Expand All @@ -29,3 +33,31 @@ jobs:
run: dotnet run --no-build -p ./test/Tests.NewtonsoftJson/Tests.NewtonsoftJson.fsproj -c Release -f netcoreapp3.1
- name: Test SystemTextJson
run: dotnet run --no-build -p ./test/Tests.SystemTextJson/Tests.SystemTextJson.fsproj -c Release -f netcoreapp3.1
docs:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Setup .NET Core 5
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.301
- name: Setup .NET Core 3
uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.405
- name: Setup .NET Core 2
uses: actions/setup-dotnet@v1
with:
dotnet-version: 2.2.402
- name: Restore dotnet tools
run: dotnet tool restore
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build -c Release
- name: Build Doc src
run: dotnet build -c Release ./docsrc/docs
- name: Render Docs
run: dotnet run --project ./docsrc/tool
1 change: 0 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
<Version Condition=" '$(VersionSuffix)' == '' ">$(VersionPrefix)</Version>
<AssemblyVersion>$(VersionPrefix).0</AssemblyVersion>
<FileVersion>$(VersionPrefix).0</FileVersion>

</PropertyGroup>

<ItemGroup>
Expand Down
111 changes: 106 additions & 5 deletions docsrc/content/codec.fsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#r "nuget: FSharpPlus"
#r "nuget: System.Json"
#r "nuget: System.Json, 4.7.1"
#r "nuget: FSharpPlus, 1.2.1"
wallymathieu marked this conversation as resolved.
Show resolved Hide resolved
#r @"../../src/Fleece.SystemJson/bin/Release/netstandard2.1/Fleece.SystemJson.dll"

open Fleece.SystemJson
open Fleece.SystemJson.Operators


(**

## CODEC

For types that deserialize to Json Objets, typically (but not limited to) records, you can alternatively use codecs and have a single method which maps between fields and values.
```f#
#r "nuget: Fleece.SystemJson"
open Fleece.SystemJson
open Fleece.SystemJson.Operators
```

For types that deserialize to Json Objets, typically (but not limited to) records, you can alternatively use codecs and have a single method which maps between fields and values.
*)

type Person = {
Expand Down Expand Up @@ -60,6 +66,12 @@ type PersonF = {
|> jfieldWith jsonValueCodec "children" (fun x -> x.children)

(**
Both approaches build a codec from the same pieces:

- A constructor function that builds a new record from deserialized pieces
- A sequence of field specifications with `jfield/jfieldOpt` or `jreq/jot`.
These specs take a field name and a function for getting that fields value from a record instance.

Discriminated unions can be modeled with alternatives:
*)

Expand Down Expand Up @@ -92,7 +104,96 @@ type ShapeC =
(**
What's happening here is that we're getting a Codec to/from a Json Object (not neccesarily a JsonValue) which Fleece is able to take it and fill the gap by composing it with a codec from JsonObject to/from JsonValue.

We can also do that by hand, we can manipulate codecs by using functions in the Codec module. Here's an example:
For DUs that carry no data, a function is still necessary:
*)

type CompassDirection =
| North
| East
| South
| West
with
static member JsonObjCodec =
jchoice
[
(fun () -> North) <!> jreq "north" (function North -> Some () | _ -> None)
(fun () -> South) <!> jreq "south" (function South -> Some () | _ -> None)
(fun () -> East) <!> jreq "east" (function East -> Some () | _ -> None)
(fun () -> West) <!> jreq "west" (function West -> Some () | _ -> None)
]


(**
A common way to represent algebraic data types in JSON is to use a type tag.
For example:
**)

let someShapes = """
[
{
"type": "rectangle",
"width": 8.8,
"length": 12.0
},
{
"type": "circle",
"radius": 37.8
},
{
"type": "prism",
"width": [10.0, 23.0],
"height": 9.10
}
]
"""

open FSharpPlus
open FSharpPlus.Operators

type ShapeD =
| Rectangle of width : float * length : float
| Circle of radius : float
| Prism of width : float * float * height : float
with
static member JsonObjCodec =
/// Derives a concrete field codec for a required field and value
let inline jreqValue prop value codec =
let matchPropValue o =
match IReadOnlyDictionary.tryGetValue prop o with
| Some a when (ofJson a) = Ok value -> Ok o
| Some a -> Decode.Fail.invalidValue a value
| None -> Decode.Fail.propertyNotFound prop o
Codec.ofConcrete codec
|> Codec.compose (
matchPropValue,
fun encoded ->
if encoded.Count=0 then encoded // we have not encoded anything so no need to add property and value
else IReadOnlyDictionary.union (Dict.toIReadOnlyDictionary (dict [prop, toJson value])) encoded
)
|> Codec.toConcrete


jchoice
[
fun w l -> Rectangle (w,l)
<!> jreq "width" (function Rectangle(w, _) -> Some w | _ -> None)
<*> jreq "length" (function Rectangle(_, l) -> Some l | _ -> None)
|> jreqValue "type" "rectangle"

Circle
<!> jreq "radius" (function Circle (r) -> Some r | _ -> None)
|> jreqValue "type" "circle"

fun (w,w2) h -> Prism (w,w2,h)
<!> jreq "width" (function Prism (x, y, _) -> Some (x, y) | _ -> None)
<*> jreq "height" (function Prism (_, _, h) -> Some h | _ -> None)
|> jreqValue "type" "prism"
]

let parsedShapedD = parseJson<ShapeD list> someShapes

(**
We can manipulate codecs by using functions in the Codec module. Here's an example:
*)
open System.Text
let pf : PersonF= {name = ("John", "Doe"); age = None; children = [{name = ("Johnny", "Doe"); age = Some 21; children = []}]}
Expand Down
9 changes: 7 additions & 2 deletions docsrc/content/combinators.fsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#r "nuget: FSharpPlus"
#r "nuget: System.Json"
#r "nuget: System.Json, 4.7.1"
#r "nuget: FSharpPlus, 1.1.1"
wallymathieu marked this conversation as resolved.
Show resolved Hide resolved
#r @"../../src/Fleece.SystemJson/bin/Release/netstandard2.1/Fleece.SystemJson.dll"

open Fleece.SystemJson

(**
```f#
#r "nuget: Fleece.SystemJson"
open Fleece.SystemJson
```

## Combinators

So far we've seen how Fleece is capable of encoding/decoding by deriving automatically a codec from static members in the type.
Expand Down
13 changes: 9 additions & 4 deletions docsrc/content/comparison-with-json-net.fsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#r @"nuget: Newtonsoft.Json"
#r "nuget: FSharpPlus, 1.1.7"
#r @"../../src/Fleece.SystemJson/bin/Release/netstandard2.1/Fleece.SystemJson.dll"
#r "nuget: Newtonsoft.Json, 10.0.2"
#r "nuget: FSharpPlus, 1.1.1"
wallymathieu marked this conversation as resolved.
Show resolved Hide resolved
#r @"../../src/Fleece.NewtonsoftJson/bin/Release/netstandard2.1/Fleece.NewtonsoftJson.dll"

(**
```f#
#r "nuget: Fleece.NewtonsoftJson"
```
*)

open System
open Newtonsoft.Json
open FSharpPlus
Expand Down Expand Up @@ -55,7 +60,7 @@ with
static member OfJson (json:Linq.JToken) =
match json with
| JObject o ->
monad {
monad.strict {
match! o .@ "type" with
| "Bike" -> return Bike
| "Car" ->
Expand Down
65 changes: 65 additions & 0 deletions docsrc/content/further-techniques.fsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#r "nuget: System.Json, 4.7.1"
#r "nuget: FSharpPlus, 1.1.1"
#r @"../../src/Fleece.SystemJson/bin/Release/netstandard2.1/Fleece.SystemJson.dll"

open Fleece.SystemJson
open Fleece.SystemJson.Operators

(**
Sometimes, the JSON required by a given situation will contain fields that do not need to be present in the F# data model.
For example, the JSON-RPC 2.0 specification requires every request/response object to carry the field `jsonrpc` with value `"2.0"`.
In a codebase that only uses JSON-RPC 2.0, why capture this field on a record?

When writing `ToJson` and `OfJson` methods for this data, handling the required field is fairly natural:

*)

type Request =
{ Method: string
MethodParams: Map<string, string>}
static member ToJson (r: Request) =
jobj [
"method" .= r.Method
"params" .= r.MethodParams
"jsonrpc" .= "2.0"
]
static member OfJson json =
match json with
| JObject o ->
let method = o .@ "method"
let methodParams = o .@ "params"
// We require the "jsonrpc" field to be present
let jsonrpc = o .@ "jsonrpc"
match method, methodParams, jsonrpc with
| Decode.Success m, Decode.Success p, Decode.Success "2.0" -> // We enforce the value of the field
// ...but do not use it in the final object
Decode.Success {
Method = m
MethodParams = p
}
| x -> Error <| Uncategorized (sprintf "Error parsing person: %A" x)
| x -> Decode.Fail.objExpected x

(**
The can also be modeled with Codecs:
wallymathieu marked this conversation as resolved.
Show resolved Hide resolved
*)

type Response =
{ Result: string option
Error: string option }
static member JsonObjCodec =
fun r e _ -> { Result = r; Error = e }
|> withFields
|> jfieldOpt "result" (fun r -> r.Result)
|> jfieldOpt "error" (fun r -> r.Error)
|> jfield "jsonrpc" (fun _ -> "2.0")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should start deprecating this verbose syntax, in favor of coming Applicative Computation Expressions in Fleece vNext.

For the moment I would use the applicative syntax with operators.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Should we include it in the docs and write that it's obsolete then?


(**
There are three parts to this.
First, the constructor is given an unused third parameter, which will receive the field required on the JSON object.
Second, the `"jsonrpc"` field is required using `jfield`; its getter always returns `"2.0"`
Finally: the fields must be in the correct order -- that is, the field specs must follow the order of the arguments in the constructor.
*)
7 changes: 3 additions & 4 deletions docsrc/content/giraffe.fsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#r "nuget: FSharpPlus"
#r "nuget: System.Json"
#r "nuget: TaskBuilder.fs,2.1.0"
#r "nuget: System.Json, 4.7.1"
#r "nuget: FSharpPlus, 1.1.1"
#r "nuget: TaskBuilder.fs, 2.1.0"
#r @"../../src/Fleece.SystemJson/bin/Release/netstandard2.1/Fleece.SystemJson.dll"

module Giraffe=
open System.Threading.Tasks
open System.IO
Expand Down
3 changes: 2 additions & 1 deletion docsrc/content/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Fleece
======

Fleece is a library intended to help with parsing and crafting specific Json without having to write data transfer objects in order to get the expected
Fleece is a library intended to help with parsing and crafting specific JSON without having to write data transfer objects in order to get the expected
representation.

### Introduction
Expand All @@ -11,6 +11,7 @@ You can get an overview of the important part of the library by reading the foll
- [ToJson and OfJson](./to-json-and-of-json.html) are the basic building blocks
- [Codec](./codec.html) let's you combine both ToJson and OfJson in one declaration
- [Combinators](./combinators.html) lets you have more control
- [Further Techniques](./further-techniques.html) describes ways of solving various JSON-wrangling problems

### Integration with Web frameworks

Expand Down
6 changes: 3 additions & 3 deletions docsrc/content/suave.fsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#r "nuget: FSharpPlus, 1.1.7"
#r "nuget: System.Json"
#r "nuget: Suave,2.5.6"
#r "nuget: System.Json, 4.7.1"
#r "nuget: FSharpPlus, 1.1.1"
#r "nuget: Suave, 2.5.6"
#r @"../../src/Fleece.SystemJson/bin/Release/netstandard2.1/Fleece.SystemJson.dll"

(**
Expand Down
6 changes: 3 additions & 3 deletions docsrc/content/to-json-and-of-json.fsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#r "nuget: FSharpPlus, 1.1.7"
#r "nuget: System.Json"
#r "nuget: System.Json, 4.7.1"
#r "nuget: FSharpPlus, 1.1.1"
#r @"../../src/Fleece.SystemJson/bin/Release/netstandard2.1/Fleece.SystemJson.dll"

open System.Json
open Fleece.SystemJson
open Fleece.SystemJson.Operators
#if FSHARPDATA
#r "nuget: FSharp.Data"
#r "nuget: FSharp.Data, 3.0.0"
#r @"../../src/Fleece.FSharpData/bin/Release/netstandard2.1/Fleece.FSharpData.dll"

open FSharp.Data
Expand Down
7 changes: 3 additions & 4 deletions docsrc/tool/manually_build_docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ cd ../../
# Restore
dotnet tool restore

#./docsrc/tool/download_nugets.sh
# Build
# dotnet build -c Release
dotnet restore
#msbuild /t:Build /p:Configuration=Debug
dotnet build
dotnet build -c Release

# Gen docs
dotnet run --project ./docsrc/tool ReleaseDocs
dotnet run --project ./docsrc/tool
# In order to release, append "ReleaseDocs" when running the command

# dotnet fsdocs "build" "--input" "docsrc/content/" "--output" "/Users/mathieu/src/fs/Fleece/docsrc/tool/../../docs" "--sourcerepo" "https://github.com/fsprojects/Fleece/tree/master" "--parameters" "root" "/Fleece/" "project-name" "Fleece" "project-author" "Mauricio Scheffer,Lev Gorodinski,Oskar Gewalli, Gustavo P. Leon" "project-summary" "Fleece is a JSON mapper for F#. It simplifies mapping from a Json library's JsonValue onto your types, and mapping from your types onto JsonValue." "project-github" "https://github.com/fsprojects/Fleece" "project-nuget" "http://nuget.org/packages/Fleece"