Using JsonProvider from Fsharp.Data for Binance-Request - json

I'm trying to work with the Binance-Connector for .NET. In the F#-Examples, we have the following:
let f =
let loggerFactory = LoggerFactory.Create(fun (builder:ILoggingBuilder) ->
builder.AddConsole() |> ignore
)
let logger = loggerFactory.CreateLogger()
let loggingHandler = new BinanceLoggingHandler(logger)
let httpClient = new HttpClient(loggingHandler)
let market = new Market(httpClient)
let result = market.TwentyFourHrTickerPriceChangeStatistics() |> Async.AwaitTask |> Async.RunSynchronously
0
result is a string and looks something like this:
{"symbol":"ETHBTC","priceChange":"0.00013400","priceChangePercent":"0.179","weightedAvgPrice":"0.07444089","prevClosePrice":"0.07467300","lastPrice":"0.07480700","lastQty":"0.04640000","bidPrice":"0.07480600","bidQty":"6.01380000","askPrice":"0.07480700","askQty":"48.54320000","openPrice":"0.07467300","highPrice":"0.07531600","lowPrice":"0.07357000","volume":"80296.33090000","quoteVolume":"5977.33041290","openTime":1650281947747,"closeTime":1650368347747,"firstId":335177449,"lastId":335313233,"count":135785}
This works as intended, but of course I want to work with the result.
So I tried to deserialize it with the JsonProvider:
type Simple = JsonProvider<result>
Which for some reason doesn't work. The error resides with and it says that the Constructor or value is not defined (FS0039)
A sample in the docs for JsonProvider is given as follows:
type Simple = JsonProvider<""" { "name":"John", "age":94 } """>
let simple = Simple.Parse(""" { "name":"Tomas", "age":4 } """)
simple.Age
simple.Name
How can I correctly cast the json to a type?
Best regards

JsonProvider<...> takes a static string parameter which is either a sample string or a sample file (local or online accessible) in order to let the provider infer the types from it.
So in your case it would be:
let [<Literal>] BinanceSample = """ [{"symbol":"ETHBTC","priceChange":"-0.00163000"}] """
type Binance = JsonProvider<BinanceSample>
Then you should be able to parse the JSON with:
let parsed = Binance.Parse(result)
PS: try to provide a JSON sample that is as complete as possible.

Related

Static member assignment and JSON parsing in F#

Is JSON parsing possible into a type data or any other form where I can access each individual item within F# using built-in Microsoft provided library?
Following is an output generated by the following code.
"{"account_type":"type1","address":"US","preferred_languages":["en","ar","cn"],"displayName":"John","last_name":"Doe"}"
type Token = {access_token: string; refresh_token: string}
type Authentication =
new() = {}
static member token = null;
member this.RequestToken(credentials) =
let url = "example.com"
let request = WebRequest.Create(url) :?> HttpWebRequest
request.Method <- "POST"
request.ContentLength <- (int64)data.Length
use requestStream = request.GetRequestStream()
requestStream.Write(data, 0, (data.Length))
requestStream.Flush()
requestStream.Close()
let response = request.GetResponse() :?> HttpWebResponse
use reader = new StreamReader(response.GetResponseStream())
let output = reader.ReadToEnd()
reader.Close()
response.Close()
request.Abort()
Authentication.token = JsonConvert.DeserializeObject<Token>(output)
// the value or constructor "Token" is not defined
Preferably in a type, for instance
type Token = {access_token: string; refresh_token: string}
Edit
Attempting using JSON.net
You will need to use an external library of some kind. If you want to get the most out of F#, you can solve this very nicely using the F# Data JSON type provider.
The type provider can infer type of JSON data from a sample, so you could parse the above data by using your sample response to guide the inference and then use the inferred type to parse more data:
open FSharp.Data
// Define a type using a sample JSON
type Account = JsonProvider<"""
{ "account_type":"type1","address":"US",
"preferred_languages":["en","ar","cn"],
"displayName":"John","last_name":"Doe"}""">
// Now, parse actual data you loaded from somewhere
let parsed = Account.Parse(data)
// Access all the members using a member generated by the type provider.
parsed.AccountType
parsed.Address
parsed.PreferredLanguages |> Seq.length

Parsing JSON With No Tag F#

I am trying to deserialize the following JSON:
{
"-L3ELSSzZPRdjCRcFTrb":{
"senderId":"SWs56OIGzMdiCjSXahzDQX8zve92",
"senderName":"alberto",
"text":"Hi"
},
"-L3EN1NW5hHWBTEGC9ve":{
"senderId":"YMM45tgFFvYB7rx9PhC2TE5eW6D2",
"senderName":"David",
"text":"Hey"
}
}
To do so I have created the following two records:
type MessageContent =
{ senderId: string
senderName: string
text: string; }
type Messages =
{
messages_list : Map<string,MessageContent>;
}
Next, I call:
let messages_json = JsonConvert.DeserializeObject<Types.Messages>(html)
however, this produces the following result:
{{messages_list = null;}}
The problem seems to be that there is no messages_list tag in the JSON, so the converter cannot find this tag and returns null. How would I handle a jSON like this though where no initial tag is available?
The easiest way of doing this is probably by using the [<JsonExtensionData>] attribute and adding [<CLIMutable>]
Change your Messages type like this (you might also have to add [<CLIMutable>] to your MessageContent type)
[<CLIMutable>]
type Messages = { [<JsonExtensionData>] messages : IDictionary<string, JToken> }
Then you can deserialize it into a map like this
let msg = JsonConvert.DeserializeObject<Messages>(html)
let messagemap =
msg.messages
|> Seq.map (fun kvp -> kvp.Key, kvp.Value.ToObject<MessageContent>())
|> Map.ofSeq
which will leave you with a map of MessageContent records.

How to create JSON format from Realm "Results" using Object Mapper

I try to create JSON format from Realm Results using Object Mapper. So, I created two generic methods to do that. Fisrt method create array form Results and looks like that:
var allRealmData: Results<Project>? // in this variable I save all Project Objects first
func makeAnArrayFromResults<T>(object: T.Type) -> [T]?{
var array = [T]()
guard let mainArray = allRealmData else { return nil }
for i in mainArray {
if let object = i as? T {
array.append(object)
}
}
return array
}
then I would like to use Object Mapper to change this array to JSON Object, but when I try do it, I receive an error and don't know how can I resolve it. My second method looks like that:
func createJSON<T: Object>(object: T.Type){
let array = makeAnArrayFromResults(object)
let json = Mapper().toJSONString(array!, prettyPrint: true) //here error
}
error info: Cannot invoke "toJSONString" with an argument list of type"([T], prettyPrint: Bool)".
Do you have any sugestions how can I create JSON from Result in Realm?
Firstly, makeAnArrayFromResults<T> is really just map:
let someRealmResults: Results<Project>?
...
let array = someRealmResults?.map { $0 } // => [Project]?
As far as the Object Mapper integration goes, it looks like you don't have a toJSONString function defined that satisfies the first argument type constraints of [Person].
There's quite a bit of discussion in Object Mapper's issue tracker about interoperability with Realm that you may find useful: https://github.com/Hearst-DD/ObjectMapper/issues/475

Parsing out a portion of a JSON file and seeing the data

I found this F# JSON parsing - How to get property using complex path (consisting of several propery names) (I shortened the file string). I am wondering how to show the actual data from the json file in the FSI window?
let soilTest = JsonValue.Load("c:\\,,,soiltest.json")
let query soilTest=
let node = JObject.Parse soilTest
node.SelectToken "person[0].name" |> string
Here is the beginning of the json file:
{
"person": [
{
"name": "John Doe",
"date": "December 1, 2015",
.....
I looked at the link that Mark Seemann provided to see how to get the data out of the json file and be shown in the console.
However when putting
Console.WriteLine(name)
I get this warning:
warning FS0020: This expression should have type 'unit', but has type 'string'. Use 'ignore' to discard the result of the expression, or 'let' to bind the result to a name.
val query : soilTest:string -> unit
Tried |>ignore at the end of console.writeline and I get val query : soulTest:string -> unit
How do you do a let statement from this point to bind the data to it?
soilTest is a of type JsonValue but you're trying to parse it as string, hence the error. Please adjust the path for your environment, otherwise this works for me:
let soilTest = JsonValue.Load(#"c:\tmp\test.json")
let qry (soilTest:JsonValue) =
let data = JObject.Parse (soilTest.ToString())
data.SelectToken "person[0].name" |> string
qry soilTest
//val it : string = "John Doe"
There might be better ways to work with. Json and xml are the bane of my existence...
Edit: e.g. you can access the properties directly, like this:
let soilTest = JsonValue.Load(#"c:\tmp\test.json")
soilTest.["person"].[0].["name"].AsString()
//val it : string = "John Doe"

Insert json string to mongoDB

I am new to Scala and MongoDB. I am writing a Scala program to read JSON string (it can be nested json string also) and insert the same to Mongo DB.
I tried below few things but its not working.
1) Tried to create an Document for the JSON string as below:
var document = (DBObject) JSON.parse(jsonString)
but getting the error
"value JSON is not member of object com.mongodb.DBObject".
2) Also tried with bson.Document as below but still can get it working
var myDoc = (Document.parse(schemajson))
Can anyone help me out on this? Please let me know if my approach is correct. If not the please do let me know what all things I need to do.
adding code:
val hadoopRDD = sc.textFile(data_file).foreach(line =>
{
data_array.clear
line.split("\t").foreach(word => data_array += word)
println(data_array)
var final_json = (schemajson.format(data_array: _*))
var document = (DBObject) JSON.parse(jsonString)
in above code final_json is the string having Json string like {"name": xyz, "age": 22}
I found the answer and below is the solution which worked for me.
I was simply using
var document = (DBObject) JSON.parse(jsonString)
which was causing error.
Instead we need to provide asInstanceOf[DBObject] which worked in my case
val dbObject: DBObject = JSON.parse(final_json_str).asInstanceOf[DBObject]