Skip to content

Commit

Permalink
75-175 returned
Browse files Browse the repository at this point in the history
  • Loading branch information
Thorium committed Mar 13, 2024
1 parent 7d074c7 commit 9ef13d1
Showing 1 changed file with 101 additions and 0 deletions.
101 changes: 101 additions & 0 deletions docs/library/JsonProvider.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,104 @@ simple.Age
simple.Name

(*** include-fsi-merged-output ***)

(**
You can see that the generated type has two properties - `Age` of type `int` and `Name` of
type `string`. The provider successfully infers the types from the sample and exposes the
fields as properties (with PascalCase name to follow standard naming conventions).
### Inferring numeric types
In the previous case, the sample document simply contained an integer and so the provider
inferred the type `int`. Sometimes, the types in the sample document (or a list of samples)
may not match exactly. For example, a list may mix integers and floats:
*)

type Numbers = JsonProvider<""" [1, 2, 3, 3.14] """>
let nums = Numbers.Parse(""" [1.2, 45.1, 98.2, 5] """)
let total = nums |> Seq.sum

(*** include-fsi-merged-output ***)

(**
When the sample is a collection, the type provider generates a type that can be used to store
all values in the sample. In this case, the resulting type is `decimal`, because one
of the values is not an integer. In general, the provider supports (and prefers them
in this order): `int`, `int64`, `decimal` and `float`.
Other primitive types cannot be combined into a single type. For example, if the list contains
numbers _and_ strings. In this case, the provider generates two methods that can be used
to get values that match one of the types:
*)

type Mixed = JsonProvider<""" [1, 2, "hello", "world"] """>
let mixed = Mixed.Parse(""" [4, 5, "hello", "world" ] """)

mixed.Numbers |> Seq.sum
mixed.Strings |> String.concat ", "

(*** include-fsi-merged-output ***)

(**
As you can see, the `Mixed` type has properties `Numbers` and `Strings` that
return only `int` and `string` values from the collection. This means that we get
type-safe access to the values, but not in the original order (if order matters, then
you can use the `mixed.JsonValue` property to get the underlying `JsonValue` and
process it dynamically as described in [the documentation for `JsonValue`](JsonValue.html).
### Inferring record types
Now let's look at a sample JSON document that contains a list of records. The
following example uses two records - one with `name` and `age` and the second with just
`name`. If a property is missing, then the provider infers it as optional.
If we want to just use the same text used for the schema at runtime, we can use the `GetSamples` method:
*)

type People =
JsonProvider<"""
[ { "name":"John", "age":94 },
{ "name":"Tomas" } ] """>

for item in People.GetSamples() do
printf "%s " item.Name
item.Age |> Option.iter (printf "(%d)")
printfn ""

(*** include-fsi-merged-output ***)

(**
The inferred type for `items` is a collection of (anonymous) JSON entities - each entity
has properties `Name` and `Age`. As `Age` is not available for all records in the sample
data set, it is inferred as `option<int>`. The above sample uses `Option.iter` to print
the value only when it is available.
In the previous case, the values of individual properties had common types - `string`
for the `Name` property and numeric type for `Age`. However, what if the property of
a record can have multiple different types? In that case, the type provider behaves
as follows:
*)

type Values = JsonProvider<""" [{"value":94 }, {"value":"Tomas" }] """>

for item in Values.GetSamples() do
match item.Value.Number, item.Value.String with
| Some num, _ -> printfn "Numeric: %d" num
| _, Some str -> printfn "Text: %s" str
| _ -> printfn "Some other value!"

(*** include-fsi-merged-output ***)

(**
Here, the `Value` property is either a number or a string, The type provider generates
a type that has an optional property for each possible option, so we can use
simple pattern matching on `option<int>` and `option<string>` values to distinguish
between the two options. This is similar to the handling of heterogeneous arrays.
Note that we have a `GetSamples` method because the sample is a JSON list. If it was a JSON
object, we would have a `GetSample` method instead.
#### More complex object type on root level

0 comments on commit 9ef13d1

Please sign in to comment.