I'm parsing JSON file using FSharp.Data.JsonProvider.
From http://fsharp.github.io/FSharp.Data/library/JsonProvider.html,
I have to declare type like this.
type Simple = JsonProvider<"path/to/simple/json.json">
But I want make the JsonProvider more flexible.
What I mean by flexible is to declare like below.
let x = "path/to/simple/json.json"
type Simple = JsonProvider<x>
So, the JsonProvider can deal with various json files, rather than only specific ones.
How can I do this? Or, there are other approaches?
If your files have the same shape (ie same fields and field types), then you can use one as the type parameter, and then pass the others to Parse:
open System.IO
type Simple = JsonProvider<"file1.json">
let value1 = Simple.GetSample()
let value2 = Simple.Parse(File.ReadAllText "file2.json")
Edit: what if they don't have the same shape? Well, there are two cases.
If they have minor differences, like some fields that may be present or absent, or may be eg a string or a number, then you can make a sample file that is an array of samples and use JsonProvider<"sample.json", SampleIsList = true>. The generated type will accommodate for the differences using optional values.
If they are completely different, then JsonProvider simply isn't the right tool for the job. The purpose of JsonProvider is to give a nice API to read JSON when you know in advance what shape it's going to have. If you don't, then you should instead use a generic JSON parser with a document type, like System.Text.Json's JsonDocument or Newtonsoft.Json's JObject. I guess if you really want to keep using JsonProvider, you could also use the document type used by JsonProvider behind the scenes (called JsonValue).
Related
(previously "using type provider from C#")
This should be easy to answer, and the underlying issue has be asked before, but I'm slightly confused about it, having dug into it, there's something I'm missing.
The Xml type provider in
https://fsharp.github.io/FSharp.Data/library/XmlProvider.html
is erased?
so the types in it can't be used from another assembly?
"Erasing Type Providers produce types that can only be consumed in the assembly or project they are generated from" (says the MS docs).
but if I define in one assembly this
module Xml
type Xml = FSharp.Data.XmlProvider<"""
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>""">
let foo () = Xml.Parse ("")
and in another assembly, reference the above assembly and go...
let x = Xml.foo ()
let s = x.From
this compiles...so if the MS docs are correct, the provider must be generative?
if I implement the same code in C#,
var x = Xml.foo();
var y = x.From;
then this code doesnt compiler
'XmlElement' does not contain a definition for 'From' and no accessible extension method 'From' accepting a first argument of type 'XmlElement'
which seems to imply its erased...or I need to do something else (I've included the same dependencies).
My hunch is its erased, but then how does the F# assembly compile?
(I want it to be generative to use from C# and VB).
As far as I can tell by looking at the source code, all the types provided by FSharp.Data are erased.
The XmlProvider type is defined by this line at https://github.com/fsharp/FSharp.Data/blob/master/src/Xml/XmlProvider.fs#L26
let xmlProvTy = ProvidedTypeDefinition(asm, ns, "XmlProvider", None, hideObjectMethods=true, nonNullable=true)
As you can see, the instantiation of the ProvidedTypeDefinition type does not include isErased=false.
The ProvidedTypeDefinition type has two constructors. See the signatures at https://github.com/fsprojects/FSharp.TypeProviders.SDK/blob/master/src/ProvidedTypes.fsi#L268-L275
Both constructors have an implementation including this line, which means that the provide type is erased by default:
let isErased = defaultArg isErased true
Without redesigning the XmlProvider, the only way you can consume it from C# is to wrap it in an F# class that exposes C#-friendly types. Not sure if that would result in a better design than just avoiding the type provider altogether.
Assuming by "erased" you mean being removed from the FSharp.Data Library? then XMLProvider its probably not removed from it because I just tried your code and it works in F# project (as you have also noticed).
Now about the C# project the thing is TypeProviders are a Fsharp specific thing, whatever magic F# compiler is doing to type check xml and access the From xml-node here is probably only exclusive to F#.
The type of you variable x here is FSharp.Data.Runtime.BaseTypes.XmlElement, looking at the documentation here and the source code here.
There doesn't seem to be any way of accessing the xml nodes in an OOP way. Its probably not meant to be accessed that way either. If you want to parse xml and read its nodes in C# there are plenty of other ways for that. One being XmlReader in System.Xml
I just started to study F# and accidentally wrote this binding
let List = 1
Now when I try to obtain List methods such as 'filter' I get this error
error FS0039: The field, constructor or member 'filter' is not defined.
Of course using method with full type name like Microsoft.FSharp.Collections.List.filter is still working.
I'm wondering why it is possible to use type name as identifier in F# and how I can set back name List to type List from Microsoft.FSharp.Collections.
When I tried to reassign like this
type List = Microsoft.FSharp.Collections.List<'T>
I get
Error FS0039: The type parameter 'T is not defined.
Thank you!
In F# you can redefine almost everything and shadow existing definitions. This applies to both types (well actually types have a different behavior regarding shadowing, they shadow their values as you open the namespaces) and values but not interchangeably since values and type (and also modules) can somehow coexist at the same time in the scope. The compiler will do his best to find out which one is.
You are not forced to, but it's a common good practice in F# not to use let bindings in uppercase.
Regarding your second question, you are using a type parameter in the right side which doesn't exist in the left side of the assignment, it should be:
type List<'T> = Microsoft.FSharp.Collections.List<'T>
But notice that filter doesn't belong to the type. It's rather defined in the List module.
You should just rename your let binding from List to something sensible - as Gustavo mentioned, your definition is shadowing the core List module from F# and there is no way to use List to refer both to your integer and to the module. Shadowing core functions will make your code pretty confusing. It's also a good idea to use camelCase for let bindings, but that's a matter of taste.
If you insist on shadowing List, then you won't be able to call List.filter using List.filter. If you wanted something shorter, you could define module alias:
module FsList = Microsoft.FSharp.Collections.List
Note that your attempt to do something similar with List<'T> does not do the same thing, because functions such as filter are in a module named List rather than being static members of the type. With this, you can call filter using FsList.filter.
I'm just integrating Siesta and I love it, it solves a lot of issues we have when using frameworks like RestKit.
What I can't get my head around is how to use the content transformers? I've looked at the docs and examples and I can't quite understand how it works, I'm also fairly new to Swift.
Looking at this example taken from another SO reply:
private let SwiftyJSONTransformer = ResponseContentTransformer(skipWhenEntityMatchesOutputType: false) {
JSON($0.content as AnyObject)
}
I can't quite understand what's going on here, there is no return value so I don't understand how content is being transformed. This might be my due to a lack of deep Swift knowledge.
I've understand how NSValueTransformer objects work in Obj-C but I can't work out how to map a response abit JSON or just a simple response body like a single string, number of boolean value to a object or type using Siesta.
We have some API responses that return just a single BOOL value in the response body while most of the other API responses are complex JSON object graphs.
How would I go about mapping these responses to more primitive types and or more complex objects.
Thanks.
Some of your confusion is basic Swift stuff. Where a closure uses $0 and contains only a single statement, the input types are inferred and the return is implicit. Thus the code in your question is equivalent to:
ResponseContentTransformer(skipWhenEntityMatchesOutputType: false) {
(content: AnyObject, entity: Entity) in
return JSON(content)
}
(Using $0.content instead of just $0 is a workaround for a maybe-bug-maybe-feature in Swift where $0 becomes a tuple of all arguments instead of just the first one. Don’t worry too much about it; $0.content is just a magic incantation you can use in your Siesta transformers.)
The other half of your confusion is Siesta itself. The general approach is as follows:
Configure a generic transformer that turns the raw NSData into a decoded but unstructured type such as String or Dictionary.
You’ll usually configure this based on content type.
Siesta includes parsing for strings, JSON, and images unless you turn it off with useDefaultTransformers: false.
Optionally configure a second transformer that turns the unstructured type into a model.
You’ll usually configure this based on API path.
Siesta doesn’t include any of this by default; it’s all per app.
For responses that are just a bare boolean, you’d probably do only #1 — depending on exactly what kind of response the server is sending, and depending on how you know it's just a boolean.
I recommend looking at the example project included with Siesta, which gives a good example of how all this plays out. You’ll see examples of both transformers that conditionally operate on the content based on its type (#1) and model-specific tranformers (#2) in that code.
Is there any way to open a namespace of types provided by a type provider implementation? I ask, because some of the generated type name paths are very long and ugly in code. I have used type abbreviations to alleviate this, but obviously this is a manual process. The F# open keyword does not support this. Is there another way? Update: as pointed out in the answer and comments this is wrong. You can open a type provided namespace. I had not realised I was looking at deeply nested types, not a namespace.
This is tricky - parameterized type providers (like the standard SQL providers or the F# Data providers for XML and JSON) need to put all types that they generate (representing tables, XML nodes, etc) inside the main generated type. So all types that you might want to use are hidden somewhere as nested types of the main type (with parameters specified).
This makes sense - if you use the type provider with multiple parameters, the types generated for each configuration have to be separate.
As #kvb points out, you cannot open a nested type, but you can use type aliases to make this a bit less painful. For example, using F# Data, I can define an alias R that lets me access all the generated domain types with just two additional characters:
#r #"..\packages\FSharp.Data.1.1.10\lib\net40\FSharp.Data.dll"
open FSharp.Data
type RssFeed = XmlProvider<"http://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml">
type R = RssFeed.DomainTypes
let printTitle (itm:R.Item) = printfn "%A" itm.Title
Is it possible to serialize a hierarchy of objects in Flex, send the binary data to a URL for storage/retrieval on/from a server, and deserialize the data to restore the objects' original state?
I know it's possible to convert the objects into an XML format (haven't tried it yet), but I'm hoping to avoid parsing XML and rebuilding the objects manually. It would be nice to have functionality which can serialize/deserialize objects to a simple binary format (I did something similar in the past in Java, though not quite as easily as I would have liked). The 'eval' function in Perl is similar to what I'm looking for, sans saving code, of course.
In pseudo-code, here's what I would like to do:
private var contentToSave:HBox = new HBox();
private function saveState(event:Event):void {
var toSave:HBox = this.contentToSave;
var data:? = /* serialize 'toSave' ActionScript classes to binary data*/;
sendDataToServer(data, filename);
}
private function restoreState(filename):void {
var data:? = getDataFromServer(filename);
var savedData:HBox = /* deserialize binary 'data' to ActionScript classes */;
this.contentToSave = savedData;
}
Take a look at ByteArray.writeObject(). which saves the passed object in AMF format into the byte array. I have not used this function too much, I don't exactly know what kind of objects it can serialize, but definitely not all.
Try the JSON based serialization package in ascorelib.
[...]but I'm hoping to avoid parsing XML and rebuilding the objects manually
AS handles XML just like any other native type. Rest assured. XML is the preferred way of dealing with data you will be pulling off and putting back on a server. Of course, the ascorelib gives you a JSON class -- so you may want to look at that as well.
The 'eval' function in Perl is similar to what I'm looking for, sans saving code, of course.
IIRC, eval is part of the ECMAScript specification (and you will find it in Javascript). But not in AS3.0. It was there to a certain extent in some previous version(s?) but is no longer supported.