Code Monkey home page Code Monkey logo

chiron's People

Contributors

colinbull avatar ctaggart avatar haf avatar kolektiv avatar mavnn avatar neoeinstein avatar ninjarobot avatar panesofglass avatar petejohanson avatar tachyus-ryan avatar twith2sugars avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

chiron's Issues

Doesn't compile on *nix

fsharpi build.fsx


/Users/henrik.feldt/dev/haf/chiron/build.fsx(1,1): warning FS0211: The search directory '/Users/henrik.feldt/dev/haf/chiron/packages/FAKE/tools' could not be found


/Users/henrik.feldt/dev/haf/chiron/build.fsx(2,1): error FS0078: Unable to find the file 'packages/FAKE/tools/FakeLib.dll' in any of
 /Library/Frameworks/Mono.framework/Versions/3.12.1/lib/mono/4.5
 /Users/henrik.feldt/dev/haf/chiron
 /Library/Frameworks/Mono.framework/Versions/3.12.1/lib/mono/4.0/

Rework underlying API for encoding/decoding

The current primary interface for using Chiron is based around functions which use statically-resolved type parameters (SRTP). This API should remain in place, but it should instead be based on an API that doesn't rely on SRTP.

  • Add symmetric encoder and decoder types:
    • type Decoder<'a> = Json -> JsonResult<'a>
    • type Encoder<'a> = 'a -> Json -> Json
  • Extract the default encoder/decoder functions from ToJsonDefaults and FromJsonDefaults into functions under Encode and Decode modules, respectively
  • Add monadic and applicative functions for Decoder<'a>
  • Add monadic functions for Encoder<'a>
  • Create a fromJson {} computation expression, adapted from json {}
  • Create a toJson {} computation expression.

Better error message on Json.read failure

        let inline read key =
                fromJson >> Json.ofResult
            =<< Json.getLensPartial (objectKeyPLens key) 

// snip

        let getLensPartial l : Json<_> =
            fun json ->
                match Lens.getPartial l json with
                | Some x -> Value x, json
                | _ -> Error "", json

returns an empty error message; it could possibly re-use they key to provide a nice error message "The property 'someKey' did not exist in the JSON structure".

Bring back mono debug symbols

It was removed in the commit to re-create the nuget. This is useful for debugging/browsing into the library when I didn't compile it myself. Commit c3cc7e6

Decimal Accuracy

Currently decimals may lose data when roundtripping between values representing number with > 128bit precision. The parser should potentially be changed to accomodate this, or potentially warn/throw.

Sample choice operator?

Another sample I'd like to see in the code base, let's say I have:

{ "entity": 45 }

OR

{ "entity": "SEK" }

And I want to match based on whether it's a string or a number; how would I do that best in FromJson?

Json.tryParse "null3" should raise Exception

Json.tryParse "null3"

val it : Choice<Json,string> = Choice1Of2 (Null ())

compare to

Json.tryParse "ddd"

val it : Choice<Json,string> =
Choice2Of2
"Error in Ln: 1 Col: 1
ddd
^
Expecting: '"', '-', '0', '[', 'false', 'null', 'true' or '{'
"

Does Chiron support generic type ?

Hello guys,

I would like to support this type. The goal is to allow me to wrap my json response in this type to avoid to repeat myself. All the sub type which are going to replace the 'T type are supported by chiron.

type JsonResult<'T> =
    { Code: int
      Content: 'T
    }

    static member ToJson (x: JsonResult<'T>) =
         Json.write "Code" x.Code
      *> Json.write<'T> "Content" x.Content

The problem is I got this error:
This code is not sufficiently generic. The type variable ^T when ( ^T or ToJsonDefaults) : (static member ToJson : ^T -> Json<unit>) could not be generalized because it would escape its scope.

Is this feature possible ? If yes could you help me please :)

[Edit]: I suppose I need to restrict the Type of 'T but don't know how.

How about deserializeWith?

  let tryDeserializeWith (fromJson : Json<'a>) : Json -> Choice<'a, string> =
    fun json ->
      match fromJson json with
      | JsonResult.Value x, _ -> Choice.create x
      | JsonResult.Error err, _ -> Choice.createSnd err

  let tryDeserializeWithP (_, fromJson : Json<'a>) : Json -> Choice<'a, string> =
    tryDeserializeWith fromJson

  let deserializeWith (fromJson : Json<'a>) : Json -> 'a =
    fun json ->
      match tryDeserializeWith fromJson json with
      | Choice1Of2 x -> x
      | Choice2Of2 err -> failwith err

  let deserializeWithP (_, fromJson : Json<'a>) : Json -> 'a =
    deserializeWith fromJson

JSON parser can't handle " " key

Error in Ln: 1 Col: 54
{"updated":{"description":"a","key":"a","settings":{"   ":"a"}}}
                                                     ^
Expecting: '"' or '\\'

But it's a valid key; isn't it? I haven't seen that JSON requires trimming the keys for whitespace.

Sign Chiron assemblies with a strong name

Have you considered signing your assemblies with a strong name? I really like your project, and it works really well with F# compared to alternatives like the built in type providers and the reliable Newtonsoft.

The problem with not signing your assemblies is that you alienate the inclusion of your library in a project that is strongly named. I can get around this temporarily, but I have to actually sign your dlls with my own key. This is a bit of a hassle, especially if I want to release my work as an open source project.

Please consider this.

Improve serialization failure reporting

Prefer JsonResult<'a> as the return type for deserialization.

  • Create a type, JsonError, consisting of all potential JSON deserialization failures
    • Include a recursive Tag case to allow structured reporting
    • Include a OtherDeserializationError to allow consumers to extend with their own failures
  • Modify JsonResult<'a> to have a JsonError list in the Error case
  • Define functor and applicative functions for JsonResult<'a>
  • Add a formatError : JsonError -> string function
  • Add a JsonResult.toOption function
  • Add a JsonResult.toChoice function

Add benchmarks for performance tuning

  • Decide on a benchmarking library that works well with xUnit
  • Develop a few representative schemas along with Chiron encoders/decoders
    • Serialize/Deserialize
      • Small records with one or two fields.
      • Large records
    • Parse/Format
      • Trivial
      • Large strings
      • Object with many properties
      • Deeply nested objects/arrays
      • Different parsing/formatting strategies
  • Determine metrics by which to determine improvement

Slow when rendering long strings

I'm trying to render long strings as json. A (minimal) example would is
String.replicate 30000 "a" |> Json.serialize |> Json.format |> printfn "%s"
The F# program (or script) with Chiron is taking much more time to render than comparable programs in, e.g., Go.

Is there anything that I could change, like maybe a more efficient string implementation, to improve the situation?

Thanks!

Write/Read nested objects

I often have a tuple in F# 3-5 items long, but want to serialise it as an object;
then I need to do lots of shenanigans.

With these I don't:

module Json =
    let writeNested key (toJson : _ -> Json<unit>) value : Json<unit> =
      fun json ->
        let inner = snd (toJson value json)
        Json.write key inner json
module Patterns =
  let inline (|InProperty|_|) key (fromJson : 'a -> Json<'a>) =
        Aether.Lens.getPartial (Json.ObjectPLens >??> Aether.mapPLens key)
     >> Option.bind (fromJson (Unchecked.defaultof<_>)
                     >> fst
                     >> function | Value x -> Some x
                                 | Error _ -> None)

Means I can have this usage:

type Interact =
  | Create of owner:Owner * createdByPrincipal:Id * bbf:Money
  | TopUp of amount:Money
  | Charge of amount:Money

  static member ToJson (i : Interact) : Json<unit> =
    let writeInner (owner, createdBy, bbf) : Json<unit> =
      Json.write "owner" owner
      *> Json.write "createdByPrincipal" createdBy
      *> Json.write "bbf" bbf

    match i with
    | Create (o, cb, bbf) -> Json.writeNested "create" writeInner (o, cb, bbf)
    | TopUp amount -> Json.write "topUp" amount
    | Charge amount -> Json.write "charge" amount

  static member FromJson (_ : Interact) : Json<Interact> =
    let inline readInner _ : Json<_ * _ * _> =
      (fun o cb bbf -> o, cb, bbf)
      <!> Json.read "owner"
      <*> Json.read "createdByPrincipal"
      <*> Json.read "bbf"

    function
    | InProperty "create" readInner (o, cb, bbf) as json ->
      Json.init (Create (o, cb, bbf)) json
    | Property "topUp" amount as json ->
      Json.init (TopUp amount) json
    | Property "charge" amount as json ->
      Json.init (Charge amount) json
    | json ->
      Json.error (sprintf "couldn't convert %A to Interact" json) json

What about adding them to the project?

Flattening nested types

I would like to 'flatten' the json output of a nested type when serializing to json. To illustrate what I mean, below is the the logic I would like to express:

type EffortData = 
    {
        lap          : int       
        distance : distance 
        splits       : SplitTracker []
    }
    static member ToJson (x:EffortData) =
        Json.write "lap" x.lap
     *> Json.write "distance" (x.distance.ToString())
     for i in 0 .. x.splits.Length do
           *> Json.write x.splits.[i].name x.splits.[i].duration

How can I accomplish this?

Dates should default to DateTimeOffset

See https://msdn.microsoft.com/en-us/library/bb384267%28v=vs.110%29.aspx

Unless a particular DateTime value represents UTC, that date and time value is often ambiguous or limited in its portability. For example, if a DateTime value represents the local time, it is portable within that local time zone (that is, if the value is deserialized on another system in the same time zone, that value still unambiguously identifies a single point in time). Outside the local time zone, that DateTime value can have multiple interpretations. If the value's Kind property is DateTimeKind.Unspecified, it is even less portable: it is now ambiguous within the same time zone and possibly even on the same system on which it was first serialized. Only if a DateTime value represents UTC does that value unambiguously identify a single point in time regardless of the system or time zone in which the value is used.

You more often want to represent something as a point-in-time, than something 'local to where you computer/server is'.

Sample deserialise union?

You have a serialise example; could you show the deserialise example?

type TestUnion =
    | One of string
    | Two of int * bool

    static member ToJson (x: TestUnion) =
        match x with
        | One (s) -> Json.write "one" s
        | Two (i, b) -> Json.write "two" (i, b)

How do you read a list of a structure?

How do I read a collection of a known type?

type Folder =
  { _url : Uri option
    email : string
    files : File list
    folders : Folder list
    id : string
    name : string }

  static member FromJson (_ : Folder) =
    (fun url email files folders id name -> 
      { _url = Some (Uri url)
        email = email
        files = files
        folders = folders
        id = id
        name = name })
    <!> Json.read "@url"
    <*> Json.read "Email"
    <*> Json.read "Files" // error: no overloads match...
    <*> Json.read "Folders"
    <*> Json.read "Id"
    <*> Json.read "Name"

I'm trying to read a File and a Folder list; File already has ToJson and FromJson.

Question: how to 'drill in' with a JSON lens?

I have a piece of data wrapped in an object naming it:

{"Voucher":{"@url":"https:\/\/
           ^^^ here starts the real object
^^^ wrapper object

I want to optionally 'unwrap' the outer object's property; what's the best way to do that?

Make Chiron more Paket file-include friendly

Add a compiler directive CHIRON_PUBLIC to distinguish between the cases where Chiron is being published as a NuGet package versus as a single-file include. This same mechanism can also allow Chiron to be included once but shared within an application.

Q: better way to write?

I've been trying to find a better way to write this with the current API, but can't think of it at the moment:

  let writeJsonBody : SessionOptions -> Json<unit> =
    List.fold (fun acc -> function
              | LockDelay dur -> acc *> Json.write "LockDelay" (Duration.consulString dur)
              | Node node     -> acc *> Json.write "Node" node
              | Name name     -> acc *> Json.write "Name" name
              | Checks checks -> acc *> Json.write "Checks" checks
              | Behaviour sb  -> acc *> Json.write "Behavior" sb
              | TTL dur       -> acc *> Json.write "TTL" (Duration.consulString dur))
              (fun json -> Value (), Json.Object Map.empty)

  let reqBody = Json.format (snd (writeJsonBody sessionOpts (Json.Null ())))

I'm mostly thinking how the last two lines with contents both declare Json data and how I have to use snd to get the Json result of the monad value.

No serialisation for byte []

Trying to add

        static member inline FromJson (_ : byte []) : Json<byte []> =
                fun x ->
                    try
                        Json.init (Convert.FromBase64String x)
                    with e ->
                        Json.error "bad base64 string"
            =<< Json.getLensPartial (stringPLens ())

Gives

screen shot 2015-04-21 at 11 29 10

Q: how to handle null in Json

When Json is something like

"{"query":{"count":1,"created":"2015-08-25T13:35:55Z","lang":"en-US","results":{"quote":{"symbol":"ORCL","AverageDailyVolume":"15153400","Change":"+0.00","DaysLow":null,"DaysHigh":null,"YearLow":"35.14","YearHigh":"46.71","MarketCapitalization":"156.45B","LastTradePriceOnly":"36.08","DaysRange":null,"Name":"Oracle Corporation Common Stock","Symbol":"ORCL","Volume":"2200","StockExchange":"NYQ"}}}}"

When I try to parse with Json.read DaysLow for example I get:

> System.Exception: couldn't use lens (<fun:totalLensPartialIsomorphism@98>, <fun:totalLensPartialIsomorphism@99-1>) on json 'Null null'
   at Microsoft.FSharp.Core.Operators.FailWith[T](String message)
   at Network.Yahoo.Finance.parseResponse(String json) in H:\YFinance.fs\src\YFinanceFs\Finance.fs:line 58
   at Network.Yahoo.Finance.getStockQuoteAsync@88-1.Invoke(String _arg1) in H:\YFinance.fs\src\YFinanceFs\Finance.fs:line 88
   at Microsoft.FSharp.Control.AsyncBuilderImpl.args@835-1.Invoke(a a)
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.FSharp.Control.AsyncBuilderImpl.commit[a](Result`1 res)
   at Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronously[a](CancellationToken token, FSharpAsync`1 computation, FSharpOption`1 timeout)
   at Microsoft.FSharp.Control.FSharpAsync.RunSynchronously[T](FSharpAsync`1 computation, FSharpOption`1 timeout, FSharpOption`1 cancellationToken)
   at Network.Yahoo.Finance.getStockQuote(String symbol) in H:\YFinance.fs\src\YFinanceFs\Finance.fs:line 105
   at <StartupCode$FSI_0003>.$FSI_0003.main@()

Using Json.readOrDefault "DaysLow" String.Empty didn't solved the problem.

If json contains a null value I'd like to supply a default. Is this possible?

Thank you.

Provide a way to call through reflection

While migrating to this library, it's useful to have interop with Newtonsoft.

Provide a way to call the serialisation infrastructure through reflection for any given type, throwing a runtime exception if serialisation functions are unavailable.

List serialization reverses the JSON array

While the round-trip serialization of a List<'a> is consistent, the JSON produced by serialization contains the serialized List<'a> in reverse order. This can cause problems if the produced JSON is used by non-Chiron consumers and the order of the elements is important.

Backwards-compatible reads

A default function for 'this property might not exist, because it was introduced later:

  (fun ... -> ())
   <!> // ...
   <*> (Json.bind (Json.tryRead "images")
                  (function | None -> Json.init []
                            | Some x -> Json.init x))

By default I think it's a good idea to be backwards-compatible with schema, so if the library's user knows that a property has been introduced later, then there should be an easy way (i.e. one that doesn't require reasoning about the monad's structure) to read that properly optionally, like above.

Feedback

In your README.md you say:

This is a fairly early work in progress, but usable enough to get feedback.

My feedback is that the library will benefit from adding even a minimal documentation, also some minimal sample(s) on README.md itself (or link to a blog post).

Otherwise code is well written and usable and fills the gap of Json functional style library.

Excellent work! ๐Ÿ˜„

I've used it for porting an Haskell library dependent on Data.Aeson to F#.

I don't like how I've traversed Json AST, but I think that this can be enhanced and made clean using Lens (and it's not up to Chiron itself).

Thanks for sharing this project.

Json.deserializeWith support?

I have some types that are out of my control and I can add a custom SomeTypeToJson and SomeTypeFromJson functions. Serialization works fine using Json.serializeWith but I've looked through the source and tests and just don't see how to use my custom deserializer. A Json.deserializeWith function would make sense, but I really don't see anything like that or any other way to use it so I think I'm just missing it. Can you point me in the right direction, please?

API Usage Question: Can I write this nicer?


  type Account =
    { _url : Uri
      active : bool
      description : string
      number : uint16
      sru : uint16
      year : uint16 }
    static member FromJson (_ : Account) =
      (fun u a d n s y ->
        { _url        = Uri(u)
          active      = a
          description = d
          number      = n
          sru         = s
          year        = y })
      <!> Json.read "@url"
      <*> Json.read "Active"
      <*> Json.read "Description"
      <*> Json.read "Number"
      <*> Json.read "SRU"
      <*> Json.read "Year"
    static member ToJson (a : Account) =
      Json.write "@url" (a._url.ToString())
      *> Json.write "Active" a.active
      *> Json.write "Description" a.description
      *> Json.write "Number" a.number
      *> Json.write "SRU" a.sru
      *> Json.write "Year" a.year

  let getAccounts (context : ConfiguredApi) : Async<_> = async {
    let! resp = stdReq context Get "/accounts" |> getResponseAsync
    let json = resp.EntityBody |> Option.map Json.parse |> Option.get
    let (page : Page), (accounts : _ list) =
      match json with
      | Object values ->
        match values |> Map.find "Accounts" with
        | Array accounts ->
          values |> Map.find "MetaInformation" |> (Json.deserialize : _ -> Page),
          accounts |> List.map (Json.deserialize : _ -> Account)
        | x -> failwithf "unexpected %A" x
      | x -> failwithf "unexpected %A" x
    return PaginatedResult (accounts, resp, page)
  }

In particular where I want to zoom in on the object and pick out pieces of it.

How to serialize a list to a Json object

Hi,

I have a question regarding a list serialization. Since I could not find any pointers in the tests or docs. I would be great if someone could point me in the right direction.

Given I have a type like this:

type Post = {
    Id: Guid
    Title: string
    Body: string
} with static member ToJson(x:Post) = json{
        do! Json.write "id" x.Id
        do! Json.write "title" x.Title
        do! Json.write "body" x.Body
}

and a list of posts like this

 let post1 = {Id= Guid.NewGuid();Title="Title hello world post 1";Body="My body"}
 let post2 = {Id= Guid.NewGuid();Title="Title hello world post 2";Body="My body"}
 let listOfPosts = [post1;post2]

    listOfPosts
        |> Json.serialize
        |> Json.formatWith JsonFormattingOptions.Pretty

this would results obviously in this

"[
  {
    "body": "My body",
    "id": "0356c901-e1fa-4bae-8fe3-70061d2e05f3",
    "title": "Title hello world post 1"
  },
  {
    "body": "My body",
    "id": "292a275b-960e-46b4-b04b-c3e214c78b0d",
    "title": "Title hello world post 2"
  }
]" 

But how can I write a custom list serialization function that would produce something like this to an object graph:

"{
  "0356c901-e1fa-4bae-8fe3-70061d2e05f3":{
    "body": "My body",
    "title": "Title hello world post 1"
  },
  "292a275b-960e-46b4-b04b-c3e214c78b0d":{
    "body": "My body",
    "title": "Title hello world post 2"
  }
}"

Thanks for any help.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.