GithubHelp home page GithubHelp logo

justinwoo / purescript-simple-json Goto Github PK

View Code? Open in Web Editor NEW
133.0 6.0 46.0 133 KB

A simple Purescript JSON library that uses types automatically

Home Page: http://purescript-simple-json.readthedocs.io

License: MIT License

PureScript 96.50% JavaScript 0.69% Nix 2.37% Shell 0.44%
purescript json type-level

purescript-simple-json's Introduction

PureScript-Simple-JSON

GitHub Workflow Status (with branch) documentation status

A simple Foreign/JSON library based on the Purescript's RowToList feature.

Quickstart

Get going quickly with the Quickstart section of the guide: https://purescript-simple-json.readthedocs.io/en/latest/quickstart.html

You may also be interested in this presentation about how Simple-JSON works well with PureScript-Record: https://speakerdeck.com/justinwoo/easy-json-deserialization-with-simple-json-and-record. Note that the slides are based on an older version of the library and on PureScript 0.11.6, and it is not necessary to understand these slides to get started.

Usage

In brief:

type MyJSON =
  { apple :: String
  , banana :: Int
  , cherry :: Maybe Boolean
  }
  
decodeToMyJSON :: String -> Either (NonEmptyList ForeignError) MyJSON
decodeToMyJSON = SimpleJSON.readJSON

See the API Docs or the tests for usage.

There is also a guide for how to use this library on Read the Docs.

Warning: Maybe

This library will decode undefined and null as Nothing and write Nothing as undefined. Please use the Nullable type if you'd like to read and write null instead. Please take caution when using Maybe as this default may not be what you want.

FAQ

How do I use this with Affjax?

Please see this page in the guide: https://purescript-simple-json.readthedocs.io/en/latest/with-affjax.html

How do I change how some fields of my JSON objects are read?

Please see this page in the guide: https://purescript-simple-json.readthedocs.io/en/latest/inferred-record-types.html

How do I work with data Types?

Please see this page in the guide: https://purescript-simple-json.readthedocs.io/en/latest/generics-rep.html

Why won't you accept my Pull Request?

Please read this appeal from another open source author: https://github.com/benbjohnson/litestream#open-source-not-open-contribution

How should I actually use this library?

James Brock has informed me that people still do not understand that this library should be used not as a library. If you do not like any of the behavior in this library or would like to opt out of some behaviors, you should copy this library into your own codebase. Please see that this libraries does not actually contain many lines of code and you should be able to learn how to construct this library from scratch with a few days of reading.

purescript-simple-json's People

Contributors

adamczykm avatar cgenie avatar dghaehre avatar dretch avatar dwhitney avatar ivantomac avatar jacereda avatar jkachmar avatar justinwoo avatar kritzcreek avatar mebubo avatar metame avatar milesfrain avatar reactormonk avatar tshinohara avatar wryk avatar xgrommx 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

purescript-simple-json's Issues

Pull requests not accepted from "fake accounts"?

I submitted a pull request about an hour or two ago.
It got rejected with the reason given "don't send me PRs from a fake account":

#83

What did you mean by a fake account?

If I've broken some rule for this repository, I'd like to know what it is.

I had a look through the history of pull requests to try to work out if I've done something wrong and noticed that at least one other pull request got rejected with the same message. But that didn't make it any clearer why my pull request got rejected or what I need to do to fix it.

Handling undefined values

Hi there 👋

I'm a little surprised about how this library uses undefined as default value for Nothing for example. Note that undefined is not a valid value in JSON. http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf (Section 5 from ECMA-404):

A JSON value can be an object, array, number, string, true, false, or null.

I'm trying to port this library to the Go backend (andyarvanitis/purescript-native-go-ffi#7) but this use of undefined is producing behavior differences because there is actually no way to produce an undefined from the standard Go library.

WriteForeign instance that omits empty object keys

Awesome library, was able to use Simple.JSON to delete 100+ lines of new type wrappers for encoding/decoding via generics!

Unfortunately, some APIs enforce a distinction between an object key not being present at all and it being present but with a value of undefined or null. In my use case, Google APIs are failing validation on {optionalParm: undefined} and {optionalParm: null} requests (via the Simple.JSON WriteForeign instances) but work fine with {}.

Would it make sense to add a newtype wrapper instance in this library to skip inserting the key at all for the WriteForeign instance? First thought was this seems like a better default for NullOrUndefined but a distinct newtype wrapper works too.

Only first error is taken into account

Hi! Thanks for the library, it's awesome!

Currently I'm facing a problem where I see that only the first error is reported, which I think is a little counter intuitive considering that the signature of readJson has MultipleErrors for error handling.

This is the complete main file i'm testing

module Main where

import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Simple.JSON (readJSON, readJSON')
import Data.Either (Either(..))
import Data.Foreign (F, MultipleErrors)
import Control.Monad.Except (runExcept)

type TestJson =
  { a :: String
  , b :: String
  }


correctJson =
  """
  {"a": "a", "b": "b"}
  """

incorrectJson = "{}"

parseTest :: String -> Either MultipleErrors TestJson
parseTest = readJSON

parseTest' :: String -> F TestJson
parseTest' = readJSON'

main :: forall e. Eff (console :: CONSOLE | e) Unit
main = do
  case (parseTest incorrectJson) of
    Right test -> log $ test.a
    Left err   -> log $ show err
  case (runExcept $ parseTest' incorrectJson) of
    Right test -> log $ test.a
    Left err   -> log $ show err

and the console outputs

> pulp run
(NonEmptyList (NonEmpty (ErrorAtProperty "a" (TypeMismatch "String" "Undefined")) Nil))
(NonEmptyList (NonEmpty (ErrorAtProperty "a" (TypeMismatch "String" "Undefined")) Nil))

I was expecting both a and b to be reported missing.

Thanks!

Object key transforms

We should probably have this since the symbol is reflected to grab the value out of the foreign object anyway.

Include "rename" function (from example) in library

This library is incredible.

What do you think about including the rename function from this example inside the library?

I've don't mind duplicating it it in my own application, but I figured I'd suggest it so others could benefit from the convenience/discoverability of having it in a centralized location.

Flesh out docs/examples in docs directory

I tripped up while following the example usage in README, and noticed that the linked telegram-bot repo might be outdated wrt the latest version of this lib - most notably, since readJSON returns a nice Either, the example usage should contain it explicitly instead of IO and F.

I'll give a try to put together a "beginner-friendly" example section PR.

Parsing Foreign Records fails

Good morning,

I have a record in a Foreign object, and when I try to turn it into a Purescript record using read' I get the following error:

Error at property "channel": Type mismatch: expected String, found Undefined

The interesting thing is that it just works if I first convert the Foreign object to a string.

Why does it behave like this?

Here's a quite large minimum failing example:

Main.js:

exports.rcrd = "{\"channel\":\"Hej där!\",\"glorp\":27,\"event\":\"mystisk uppenbarelse\",\"payload\":\"Just det...\"}";

Main.purs:

module Main where

import Prelude

import Control.Monad.Except (runExcept)
import Data.Either (Either(..))
import Data.Foldable (foldMap)
import Effect (Effect)
import Effect.Console (log, logShow)
import Foreign (Foreign, readString, renderForeignError, unsafeFromForeign)
import Simple.JSON (read', readJSON')

foreign import rcrd :: Foreign

iPrintRecords :: { channel :: String, glorp :: Int, event :: String, payload :: Foreign } -> Effect Unit
iPrintRecords r = log r.channel *> logShow r.glorp

main :: Effect Unit
main = do
  log "Hi! :D"
  log $ unsafeFromForeign rcrd
  log "----------------------------------------------------------------------------------------"
  log "This fails:"
  case runExcept (read' rcrd) of
    Left es -> log (foldMap renderForeignError es)
    Right r -> iPrintRecords r

  log "\nThis works:"
  case runExcept (readJSON' =<< readString rcrd) of
    Left es -> log (foldMap renderForeignError es)
    Right r -> iPrintRecords r

How to use this library with Affjax?

I know that might not be an issue with the library itself, but as Affjax is probably the most famous purescript library for ajax, it would make sense to be able to easily use Simple.JSON with it.

Currently I am getting the following error:

 Could not match type

    NonEmptyList ForeignError

  with type

    ResponseFormatError

It would be nice to have an easy way to have this transformation.

This is my code:

type MyRecordAlias =
  { userId :: Int
  , id :: Int
  , title :: String
  , completed :: Boolean
  }

main = void $ launchAff_ $ do
  res <- AX.request (
    AX.defaultRequest
    {
      url = "https://jsonplaceholder.typicode.com/todos/1",
      method = Left GET,
      responseFormat = ResponseFormat.string
    }
  )
  case res.body >>= JSON.readJSON  of
    Right (r :: MyRecordAlias) -> do
      log "all good"
    Left e -> do
      log "all bad"

WriteForeign NonEmptyList?

Hey Justin!
I'm trying to get errors out of this thing cleanly.
what do you think about writeforeign NonEmptyList a, MultipleErrors, Either, F?

Thanks
A

add String Literal type

Somehow ends up being quite often needed, so I will probably add this as

data StringLiteral (s :: Symbol)

and so it can be constructed with a defined mkStringLiteral function by passing in an SProxy

Cutting down on boilerplate

Hey there,

I am fairly new to Purescript, and I don't know if this is possible:

I find myself writing newtype declarations like this:

newtype ImageUrl = ImageUrl String
derive instance ntImageUrl :: Newtype ImageUrl _
derive instance genImageUrl ∷ Generic ImageUrl _
instance showSite :: Show ImageUrl where
  show = genericShow
derive newtype instance writeForeignImageUrl :: WriteForeign ImageUrl
derive newtype instance readForeignImageUrl :: ReadForeign ImageUrl

Now I could make that into a snippet in my editor but it would start to feel an awful lot like:
Generate Getters/Setters in Java.

Is there any way to achieve this with less boilerplate? Maybe... TemplatePurescript 😮 ?

Affjax example does not compile

This example:

import Prelude

import Affjax (get)
import Affjax.ResponseFormat (ResponseFormatError(..), string)
import Data.Bifunctor (lmap)
import Data.Either (Either(..))
import Data.List.NonEmpty (singleton)
import Effect.Aff (launchAff_)
import Effect.Class.Console (log)
import Simple.JSON (readJSON)

type MyRecordAlias = { userId :: Int }

main = void $ launchAff_ $ do
  res <- get string "https://jsonplaceholder.typicode.com/todos/1"
  case lmap transformError res.body >>= readJSON of
    Right (r :: MyRecordAlias) -> do
      log "all good"
    Left e -> do
      log "all bad"

transformError (ResponseFormatError e _) = singleton e

Produces this error at res.body:

  Could not match type

    Record

  with type

    Either Error


while trying to match type { body :: t0 ResponseFormatError t1
                           | t2
                           }
  with type Either Error
              { body :: String
              , headers :: Array ResponseHeader
              , status :: StatusCode
              , statusText :: String
              }
while checking that expression res
  has type { body :: t0 ResponseFormatError t1
           | t2
           }
while checking type of property accessor res.body
in value declaration main

How to handle arbitrary String Maps/JSON

I'm in a server setting. I find myself needing to route through some dynamic parts of a 3.6MB JSON response to the caller. So something like a field attributes :: Array JsonValue. Can I do this with simple-json?

Creating enumWriteForeign?

I've created an enumReadForeign from reading the docs but I'm wondering if I would have to do something similar to account for the from or is there a simpler way?

Provide examples of decoding recursive types

I would like to decode some arbitrarily deep trees with this library.

Should I be using the Generics-Rep approach outlined in the documentation?

data Foo = Foo {
    bar :: Maybe Bar
}

data Bar = Bar {
    foo :: Maybe Foo
}

derive instance genericFoo :: GR.Generic Foo _
derive instance genericBar :: GR.Generic Bar _

instance readForeignFoo :: JSON.ReadForeign Foo where
    readImpl f = GR.to <$> ?help f

-- continue to fill in the implementation holes for readForeignFoo and define a readForeignBar

Could this use case be added to the documentation?

How to readJSON for completely empty input?

Hey there,

I'm using Affjax to execute requests, defining the response format as String, and then using readJSON to parse the response into some type defined by the caller. I have a request, though, where the success response is nothing, i.e. completely empty. I tried defining the a in readJSON to be String, Maybe String, Nullable String, Foreign, etc but it all returns a parse error on the readJSON call. What I think I need is an instance for ReadForeign Unit that looks like:

instance readForeignUnit :: ReadForeign Unit where
  readImpl = pure unit

such that callers that expect the response to be completely empty can define that in the type and have a useless result be returned back in that slot. Can you foresee any issues with this or do you have any other suggestions for what to do here?

Quickstart doc doesn't define WithNullable

quickstart.md doesn't define WithNullable. I searched the repo and found a definition of it in QuickStart.purs. Maybe there should be a script to insert the code into the doc, to keep them in sync?

Default empty Arrays

What do you think about using [] as a "default" array value and allowing the key to be missing when parsing? (or maybe even empty or mempty for any valid types)

Not sure if this has drawbacks I'm not thinking of, but it'd sure be nice to get rid of a bunch of my Maybe (Array a) fields. Nothing and [] are kind of the same thing 🙂

Use ErrorAtIndex

The library uses ErrorAtProperty for record decoding, but it could also use ErrorAtIndex for Arrays.

> runExceptT $ readJSON' "{ \"foo\": [\"bar\", \"baz\", 3] }" :: F { foo :: Array String }
(Identity (Left (NonEmptyList (NonEmpty (ErrorAtProperty "foo" (TypeMismatch "String" "Number")) Nil))))

Needs a quick start guide in docs

Should probably be written by someone other than me. Maybe the only thing that really needs to be covered is

main = do
  let e = JSON.readJSON "1"
  case e of
    Right (x :: Int) -> -- ...

writeJson

This library really needs a write Json implementation.

@paf31 is it okay with you if I write an implementation into this library? I feel like it might not be kosher for me to just put it in here since I saw your implementation first.

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.