It is my first question on SO...so do not judge strictly =)
Usually all my questions techout in chat rooms (believe me, a lot of them =)).
Recently, we are talking about the RosettaCode. And I wondered to complement some of the tasks code to F#
One of them is JSON.
One of the possible solutions is the use of "F# Data: JSON Parser". So my question is linked with it.
This code works well:
open FSharp.Data
open FSharp.Data.JsonExtensions
type Person = {ID: int; Name:string}
let json = """[ { "ID": 1, "Name": "First" }, { "ID": 2, "Name": "Second" }]"""
json |> printfn "%s"
match JsonValue.Parse(json) with
| JsonValue.Array(x) ->
x |> Array.map(fun x -> {ID = System.Int32.Parse((x?ID).ToString()); Name = (string x?Name)})
| _ -> failwith "fail json"
|> Array.iter(fun x -> printfn "%i %s" x.ID x.Name)
Print:
[ { "ID": 1, "Name": "First" }, { "ID": 2, "Name": "Second" }]
1 "First"
2 "Second"
But it
{ID = System.Int32.Parse((x?ID).ToString()); Name = (string x?Name)}
doesn't look good.
This I read about JsonExtensions,
but when I use
{ID = (x?ID.AsInteger()) ; Name = (x?Name.AsString()) }
I get compile errors:
The field, constructor or "AsInteger" is not defined
The field, constructor or "AsString" is not defined
Strangely, thing is that I see accessibility through "open FSharp.Data.JsonExtensions"
So, question: How to use JsonExtensions?
I tried to reproduce this using a minimal example, but I do not get the error - can you try the following minimal sample?
#r "...../FSharp.Data.dll"
open FSharp.Data.JsonExtensions
open FSharp.Data
JsonValue.Parse("A").AsArray()
|> Array.map (fun a -> a?ID.AsInteger())
I do not get auto-completion on a?ID. (which is a limitation of the editor), but it compiles fine.
The only reason why I think this could be not working is if you had another open declaration that would import another implementation of the ? operator that is not returning JsonValue.
The JsonValue API is certainly not as nice as just using the type provider - so if you can, I'd probably go for the type provider instead (the low-level API is good if you need to iterate over everything in JSON recursively).
Related
I have a JSON input I want to parse in F# using Fsharp.Data, it looks like this:-
[
{
"Server": "ServerName1",
"CNAME": [ "cname1", "cname2"]
},
{
"Server": "ServerName2",
"CNAME": {}
}
]
Note the way an empty array is returned as {} rather than [], unfortunately I can't change this.
When I parse this with Fsharp.Data the CName property is of type ArrayOrCname (which makes sense).
How do I determine in F# whether I have an Array or an empty CName record?
The generated type ArrayOrCname should have two properties - Record and Array - which are both optional. If the value is a record, the first one will be Some and the other will be None and vice versa for an array. So, you can handle this using pattern matching. Using your example:
#r "nuget: FSharp.Data"
open FSharp.Data
type T = FSharp.Data.JsonProvider<"""[
{ "Server": "ServerName1",
"CNAME": [ "cname1", "cname2"] },
{ "Server": "ServerName2",
"CNAME": {} } ]""">
We can now get sample data (just to test this) and pattern match on the properties of Cname:
let servers = T.GetSamples()
for server in servers do
match server.Cname.Record, server.Cname.Array with
| Some recd, _ ->
printfn "empty record"
| _, Some arr ->
printfn "names: %A" arr
| _ ->
failwith "This should never happen"
If you just want to know whether the value is an array or not, you could define a simple helper that turns ArrayOrCname into an option:
let asOption (v:T.ArrayOrCname) =
match v.Record, v.Array with
| _, Some arr -> Some arr
| _ -> None
I am trying to use the Json type provider to create types and iterate over a list. But, I also need to access the serialised json of each item.
Here is what I have
#r "nuget: FSharp.Data"
#r "nuget: System.Text.Json"
open FSharp.Data
open System.Text.Json
type Users = JsonProvider<"""{"users":[{"id":1, "name": "John"}, {"id":2, "name": "Paul"}]}""">
let data = Users.GetSample().Users
let printProduct (id:int, userJson: string) =
printfn "%i %s" id userJson
let returnIdandJson() =
data |> Seq.map(fun x-> (printProduct (x.Id, JsonSerializer.Serialize x)))
returnIdandJson()
This prints
1 {"JsonValue":{"Tag":3,"IsString":false,"IsNumber":false,"IsFloat":false,"IsRecord":true,"IsArray":false,"IsBoolean":false,"IsNull":false,"_Pri:false,"IsNull":false,"_Print":"{\r\n \u0022id\u0022: 1,\r\n \u0022name\u0022: \u0022John\u0022\r\n}"}}
2 {"JsonValue":{"Tag":3,"IsString":false,"IsNumber":false,"IsFloat":false,"IsRecord":true,"IsArray":false,"IsBoolean":false,"IsNull":false,"_Pri:false,"IsNull":false,"_Print":"{\r\n \u0022id\u0022: 2,\r\n \u0022name\u0022: \u0022Paul\u0022\r\n}"}}
What I want is
1 {"id":1, "name": "John"}
2 {"id":2, "name": "Paul"}
Is there a way I can access and serialise the inner list items from JsonValue?
Thanks
You can get access to the original JSON via the .JsonValue property that exists on every object returned by the provider. This property returns type JsonValue, which you can programmatically analyze if you wanted (e.g. calling its Properties() method or some such), and it also has a handly ToString implementation that just returns the serialized JSON:
let printProduct (id:int, userJson: string) =
printfn "%i %s" id userJson
let returnIdandJson() =
data |> Seq.map(fun x-> (printProduct (x.Id, string x.JsonValue)))
I am trying to make an immutable map of
in C#, I can turn a dictionary into a readonly one, achieving the same.
but it looks like, in F#, the first element decides the type of the rest of the map.
so I tried something ugly:
Map [
"key1", value1 |> obj
"key2", value2 |> obj
or
Map [
"key1", box value1
"key2", box value2
is there a better solution where the compiler would automatically box the items?
The problem I am trying to solve is to talk to an external C# library expecting a Dictionary and I'm trying to learn if the F# compiler can do this kind of things.
This question already has an answer, albeit for Dictionary<_,obj> rather than Map<_,obj>.
let (=>) k v = k, box v
// val ( => ) : k:'a -> v:'b -> 'a * obj
Map [ "key1" => 1; "key2" => 'a' ]
// val it : Map<string,obj> = map [("key1", 1); ("key2", 'a')]
The help for function Series.hasNot in Deedle says:
Returns true when the series does not contains value for the specified key
The function does not seem to be working this way in the following example:
let election =
[ "Party A", 304
"Party B", 25
"Party C", 570
"Party Y", 2
"Party Z", 258 ]
|> series
let bhnt =
election
|> Series.hasNot "Party A"
printfn "%A" <| bhnt
// true
// val bhnt : bool = true
// val it : unit = ()
Am I missing something?
I just looked at the Deedle source and saw the following:
let has key (series:Series<'K, 'T>) = series.TryGet(key).HasValue
let hasNot key (series:Series<'K, 'T>) = series.TryGet(key).HasValue
Yes, you've found a bug. The hasNot function should have looked like not (series.TryGet(key).HasValue).
Workaround: Until this bug is fixed, you can work around it by replacing all occurrences of Series.hasNot key in your code by Series.has key and then piping through the not function. E.g.,
let bhnt =
election
|> Series.has "Party A"
|> not
Or, if you think it looks better, you could also write that as:
let bhnt =
election
|> (not << Series.has "Party A")
These two ways of writing it are equivalent; which one you prefer to use will depend on how comfortable you are with functional programming. Some people find the << syntax more natural to read, while others find it completely weird and want to stick to using only |>. It all depends on how experienced you are with functional programming; pick whichever of these two feels the most natural to you.
What is the collection initializer syntax in F#? In C# you can write something like:
new Dictionary<string, int>() {
{"One", 1},
{"two", 2}}
How do I do the same thing in F#? I suppose i could roll my own syntax, but seems like there should be a built-in or standard one already.
To elaborate a bit on collection initialization in F#, here are a few examples:
read-only dictionary
dict [ (1, "a"); (2, "b"); (3, "c") ]
seq (IEnumerable<T>)
seq { 0 .. 99 }
list
[1; 2; 3; 4; 5]
set
set [1; 2; 3; 4; 5]
array
[| 1; 2; 3; 4; 5 |]
As Jared says, there is no built-in support for this for arbitrary collections. However, the C# code is just syntactic sugar for Add method calls, so you could translate it to:
let coll = MyCollectionType()
["One", 1; "Two", 2] |> Seq.iter coll.Add
If you want to get fancy, you could create an inline definition to streamline this even further:
let inline initCollection s =
let coll = new ^t()
Seq.iter (fun (k,v) -> (^t : (member Add : 'a * 'b -> unit) coll, k, v)) s
coll
let d:System.Collections.Generic.Dictionary<_,_> = initCollection ["One",1; "Two",2]
I don't believe F# has an explicit collection initializer syntax. However it's usually very easy to initialize F# collections. For example
let map = [ ("One", 1); ("Two", 2) ] |> Map.ofSeq
Getting to BCL collections is usually a bit more difficult because they don't always have the handy conversion functions. Dictionary<TKey, TValue> works though because you can use the LINQ method
let map =
let list = [ ("One", 1); ("Two", 2) ]
System.Linq.Enumerable.ToDictionary(list, fst, snd)
You can use the same :
open System.Collections.Generic
Dictionary<int, string>(dict [ (1, "a"); (2, "b"); (3, "c") ])
Cheers.
The lack of a collection initializer is annoying for some XAML-centric APIs like Workflow 4.0 which rely on collection initializers instead of ctors, e.g.
new Sequence { Activities = { WriteLine { Text = "In the sequence!" } } };
In such cases, imperative .Add() is awkward because the value is conceptually declarative even though it's technically mutable/imperative. However, there's no common base class for the set of all activities which declare an Activities child: the "Activities" member is a pattern and not an interface, so you can't just write a normal helper function which adds children to any activity. Fortunately, F# member constraints come to the rescue.
In order to write this:
Sequence() |> add [Sequence(DisplayName="InnerSeq"); WriteLine(Text = InArgument<_>("In the sequence!"))]
You first need to define an inline helper function called "add":
let inline add (children: Activity seq) =
let inline doAdd (activity: ^Activity) : ^Activity when ^Activity : (member get_Activities : unit -> Activity Collection) =
let collection = (^Activity : (member get_Activities : unit -> Activity Collection) (activity))
for child in children do
collection.Add(child)
activity
doAdd
This still isn't quite as nice as the C# syntax but at least it's still declarative. IMHO this is not so much as a fault with F# as with collection-initializer-centric APIs, but at least F# allows a workaround.
Given that the C# collection initializer syntax is syntactic sugar for calling .Add and that implies a mutable collection - I'm not sure you'll see any such syntax in F#. It's initialize all in one go as per JaredPar's answer, or do it manually.