Static member assignment and JSON parsing in F# - json

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

Related

Using JsonProvider from Fsharp.Data for Binance-Request

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.

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

JsonSlurper returns No signature of method: groovy.json.JsonSlurper.parseText() is applicable for argument types: (java.util.ArrayList)

I'm trying to parse JSON file with JsonSlurper.parseText but keep getting similar problems.
def jsonParse = null
def http = new HTTPBuilder(url)
http.auth.basic(username, password)
http.request(Method.GET) {
response.success = { resp, reader ->;
jsonParse = new JsonSlurper().parseText(reader)
}
}
Whenever I run my application the error message says
No signature of method: groovy.json.JsonSlurper.parseText() is applicable for argument types: (java.util.ArrayList)
I understand that JsonSlurper.parseText() is asking for a java.util.ArrayList type as an input. So I tried the following to figure out the type of the input using this code.
def jsonParse = null
def http = new HTTPBuilder(url)
http.auth.basic(username, password)
http.request(Method.GET) {
response.success = { resp, reader ->;
jsonParse = reader
}
}
render jsonParse.getClass()
This prints out the following:
class java.util.ArrayList
I don't understand why I'm getting this error when I am feeding the input with correct datatype.
Any suggestions?
According to the documentation, the HTTPBuilder could be parsing your JSON for you. If your JSON response has its root as a JSON array, then that explains the ArrayList object in your reader variable.
Regarding how this explains the exception being thrown. The reader parameter of the Closure is an ArrayList of parsed JSON, not a String of unparsed JSON. Thus, the code fails on new JsonSlurper().parseText(reader) because reader is not text and the JsonSlurper does not have a method defined for how to parse an ArrayList as JSON.

How to convert a stream back to json?

In a .NET 3.5 Compact Framework / Windows CE app, I need to consume some WebAPI methods that return json. RestSharp looks like it would be great for this, except that it's not quite CF-ready (see Is Uri available in some other assembly than System in .NET 3.5, or how can I resolve Uri in this RestSharp code otherwise? for details).
So, I will probably use HttpWebRequest. I can return the value from the WebAPI methods with this code:
string uri = "http://localhost:48614/api/departments";
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
var webResponse = (HttpWebResponse)webRequest.GetResponse();
if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 0))
{
StreamReader reader = new StreamReader(webResponse.GetResponseStream());
MessageBox.Show("Content is " + reader.ReadToEnd());
}
else
{
MessageBox.Show(string.Format("Status code == {0}", webResponse.StatusCode));
}
...but in order to use what's returned from reader.ReadToEnd():
...I need to convert it back to json so that I can then query the data with LINQ to JSON using either JSON.NET (http://json.codeplex.com/) or SimpleJson (http://simplejson.codeplex.com/)
Is that realistically possible (converting StreamReader data to JSON)? If so, how?
UPDATE
I'm trying to deserialize the "json" (or string that looks like json) with this code:
string uri = "http://localhost:48614/api/departments";
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
webRequest.Method = "GET";
var webResponse = (HttpWebResponse)webRequest.GetResponse();
if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 0))
{
StreamReader reader = new StreamReader(webResponse.GetResponseStream());
DataContractJsonSerializer jasonCereal = new DataContractJsonSerializer(typeof(Department));
var dept = (Department)jasonCereal.ReadObject(reader.ReadToEnd());
MessageBox.Show(string.Format("accountId is {0}, deptName is {1}", dept.AccountId, dept.DeptName));
}
...but get two err msgs on the "var dept =" line:
0) The best overloaded method match for 'System.Runtime.Serialization.XmlObjectSerializer.ReadObject(System.IO.Stream)' has some invalid arguments
1) Argument '1': cannot convert from 'string' to 'System.IO.Stream'
So reader.ReadToEnd() returns a string, and DataContractJsonSerializer.ReadObject() expects a stream, apparently. Is there a better approach for this? Or, if I'm on the right track (although currently a section of track has been removed, so to speak), how should I get past this hurdle?
UPDATE 2
I added the System.Web.Extensions reference and then "using System.Web.Script.Serialization;" but this code:
JavaScriptSerializer jss = new JavaScriptSerializer();
var dept = jss.Deserialize<Department>(s);
MessageBox.Show(string.Format("accountId is {0}, deptName is {1}",
dept.AccountId, dept.DeptName));
...but the second line fails with:
"Type 'bla+Department' is not supported for deserialization of an array."
What type should receive the call to jss.Deserialize()? How is it defined?
Well,
the ReadToEnd() method is used to read the stream into a string and output it. If you need a stream out to pass it to a method requiring a stream, you shouldn't use this method.
From what I read on this page , it seems the BaseStream property of your reader would be more appropriate to use then.

Couchbase - deserialize json into dynamic type

I'm trying to deserialize some JSON coming back from couchbase into a dynamic type.
The document is something like this so creating a POCO for this would be overkill:
{
UsersOnline: 1
}
I figured that something like this would do the trick, but it seems to deserialize into a dynamic object with the value just being the original JSON
var jsonObj = _client.GetJson<dynamic>(storageKey);
results in:
jsonObj { "online": 0 }
Is there anyway I can get the couchbase deserializer to generate the dynamic type for me?
Cheers
The default deserializer for the client uses .NET's binary serializer, so when you save or read a JSON string, it's just a string. GetJson will always just return a string. However, there are a couple of options:
You could convert JSON records to Dictionary instances:
var appJson = "{ \"UsersOnline\" : 1, \"NewestMember\" : \"zblock\" }";
var result = client.ExecuteStore(StoreMode.Set, "userCount", appJson);
var item = client.GetJson<Dictionary<string, object>>("userCount");
Console.WriteLine("There are {0} users online. The newest member is {1}.",
item["UsersOnline"], item["NewestMember"]);
Or you could use a dynamic ExpandoObject instance:
var appJson = "{ \"UsersOnline\" : 1, \"NewestMember\" : \"zblock\" }";
var result = client.ExecuteStore(StoreMode.Set, "userCount", appJson);
dynamic item = client.GetJson<ExpandoObject>("userCount");
Console.WriteLine("There are {0} users online. The newest member is {1}.",
item.UsersOnline, item.NewestMember);
In either case you're losing static type checking, which seems like it's OK for your purposes. In both cases you get access to the JSON properties without having to parse the JSON into a POCO though...
Edit: I wrote a couple of extension methods that may be useful and blogged about them at http://blog.couchbase.com/moving-no-schema-stack-c-and-dynamic-types