elm / json Goto Github PK
View Code? Open in Web Editor NEWWork with JSON in Elm
Home Page: https://package.elm-lang.org/packages/elm/json/latest/
License: BSD 3-Clause "New" or "Revised" License
Work with JSON in Elm
Home Page: https://package.elm-lang.org/packages/elm/json/latest/
License: BSD 3-Clause "New" or "Revised" License
elm repl
> import Json.Decode as JD
> import Json.Encode as JE
> JD.decodeString (JD.index -1 JD.value) "[]" |> Result.map (\v -> JE.object [("a", JE.null)] == v)
TypeError: Cannot read property 'a' of undefined
> JE.list identity [ JE.object [ ("a", JE.null) ] ] == JE.list JE.int []
TypeError: Cannot read property 'a' of undefined
> JE.object [ ("a", JE.object [ ( "a", JE.null ) ] ) ] == JE.object []
TypeError: Cannot read property 'a' of undefined
> JE.object [ ("$", JE.string "Set_elm_builtin" ) ] == JE.object [ ("$", JE.string "Set_elm_builtin" ) ]
TypeError: Cannot read property '$' of undefined
The above examples fail because the value on the right side of ==
is undefined
.
Here’s an Ellie by @mpizenberg: https://ellie-app.com/cLXHgSHm8yTa1
The problem seems to be that _Utils_eqHelp
assumes both its arguments are of the same type/shape. In general that’s true in Elm, but not for Json.Decode.Value
. It’s a wrapper around any value, which might not be the same on both sides.
Edit: This Discourse post by Evan explains that (==)
shouldn’t be used with JSON values from elm/json and an idea for improving this in the future: https://discourse.elm-lang.org/t/function-equality/7538/24
Edit 2: Apparently this is even documented! 🤦 https://package.elm-lang.org/packages/elm/core/latest/Basics#equality
Though these crash for a different reason (“the implementation is not at all made for Json.Decode.Value
”) than mentioned in those docs.
I can't understand how to write a Maybe-decoder which would succeed upon either an absent field or a null-key:
> Decode.decodeString (Decode.oneOf [Decode.nullable (Decode.field "foo" Decode.int), Decode.field "foo" (Decode.nullable Decode.int)]) """{"foo": 123}"""
Ok (Just 123) : Result Decode.Error (Maybe Int)
> Decode.decodeString (Decode.oneOf [Decode.nullable (Decode.field "foo" Decode.int), Decode.field "foo" (Decode.nullable Decode.int)]) """{"foo": null}"""
Ok Nothing : Result Decode.Error (Maybe Int)
> Decode.decodeString (Decode.oneOf [Decode.nullable (Decode.field "foo" Decode.int), Decode.field "foo" (Decode.nullable Decode.int)]) """{}"""
Err (OneOf [OneOf [Failure ("Expecting null") <internals>,Failure ("Expecting an OBJECT with a field named `foo`") <internals>],Failure ("Expecting an OBJECT with a field named `foo`") <internals>])
: Result Decode.Error (Maybe Int)
Why does it fail in the last case? How do I make it work?
Thank you.
From jsonlines.org: JSON Lines is a convenient format for storing structured data that may be processed one record at a time.
jsonlines are just, well, multiple one-liner JSON values separated by newlines.
Is there a way to parse it with this library?
There seems to be an issue with list equality check. It looks like the function functions want to work with lists (JS arrays) to get a length of a list and then compare each element one by one. But it actually gets objects with a list representation. Thus it always goes to return true;
branch.
This creates another issues.
One way to solve it is to call _Json_listEquality
function recursivelly.
The oneOf
decoder stores an Elm list of decoders, but when comparing for equality these lists are compared with _Json_listEquality
, which compares JS arrays. The result is that oneOf
decoders always compare as equal.
I noticed this when a custom event decoder was not being replaced as I expected. Here's an SSCCE that demonstrates such a case.
module Main exposing (main)
import Browser
import Html exposing (Html)
import Html.Events
import Json.Decode
main : Program () Int Int
main =
Browser.sandbox
{ init = 0
, view = view
, update = always
}
view : Int -> Html Int
view clicks =
Html.button
[ Html.Events.on "click"
-- good:
--(Json.Decode.succeed (clicks + 1))
--
-- bad:
(Json.Decode.oneOf [ Json.Decode.succeed (clicks + 1) ])
]
[ Html.text (String.fromInt clicks)
]
See https://github.com/elm/json/blob/master/src/Json/Decode.elm#L37
Points to older version that is not 0.19 compatible.
I have the following Type called Message
with the following code
module Types.Message exposing (Message, decode, decodeList)
import Json.Decode as Decode exposing (Decoder, Error, list)
import Json.Decode.Pipeline exposing (required)
type alias Message =
{ username : String
, content : String
}
decode : Decoder Message
decode =
Decode.succeed Message
|> required "username" Decode.string
|> required "content" Decode.string
decodeList : Decoder (List Message)
decodeList =
list decode
I try to decode using something like this
decodeMessage : Decode.Value -> Result Error (List Message)
decodeMessage messageJson =
Decode.decodeValue
Message.decodeList
messageJson
I found out that it works perfectly in REPL
> decodeString decode "{\"username\": \"mantap\", \"content\": \"lalui\"}"
Ok { content = "lalui", username = "mantap" }
: Result Error Message
However, when I run the Webpack build using the elm-webpack-loader
in Electron environment I found the following error
Problem with the value at json[0]: "{\"content\": \"Budi\", \"username\": \"UHU\"}" Expecting an OBJECT with a field named `content`
Any idea why?
The docs for Json.Decode.errorToString
still says “we cannot have any HTML dependencies in elm/core
”, this should probably be changed to “we cannot depend on elm/html
”.
Related to elm/file#8
Using the latest version of elm/file with the lastest version of elm/json causes file selection to stop working. The root cause is a bad piece of code that appears to have been added for node.js environments.
If the index given to Json.Decode.index
is too large we get this message:
> JD.decodeString (JD.index 1 JD.int) """[1]""" |> Result.mapError JD.errorToString
Err ("Problem with the given value:\n\n[\n 1\n ]\n\nExpecting a LONGER array. Need index 1 but only see 1 entries")
: Result String Int
In other words, there is an out-of-bounds check that produces that nice message. But the index can also be out-of-bounds by being negative. In that case, there is currently no check for this, so array[-1]
(which is undefined
) is attempted to be decoded:
> JD.decodeString (JD.index -1 JD.int) """[1]""" |> Result.mapError JD.errorToString
Err ("Problem with the value at json[-1]:\n\n undefined\n\nExpecting an INT")
: Result String Int
That’s still an error message, but not quite as nice. But using -1 doesn’t always fail:
> JD.decodeString (JD.index 1 (JD.succeed ())) """[1]""" |> Result.mapError JD.errorToString
Err ("Problem with the given value:\n\n[\n 1\n ]\n\nExpecting a LONGER array. Need index 1 but only see 1 entries")
: Result String ()
> JD.decodeString (JD.index -1 (JD.succeed ())) """[1]""" |> Result.mapError JD.errorToString
Ok () : Result String ()
I think the error message could be nicer for a negative index. Similar to how oneOf
has a nice error message for an empty list:
> JD.decodeString (JD.oneOf []) """true""" |> Result.mapError JD.errorToString
Err ("Ran into a Json.Decode.oneOf with no possibilities!")
Maybe something like this: "Ran into a Json.Decode.index with a negative index: -1".
In summary:
Note: I only noticed this because I’m re-implementing this package in Elm as a learning exercise. I’ve never accidentally passed a negative index in real code.
Suggestion: Add a function (perhaps called error
) that is like fail
, except it receives an Error
value instead of a String
as its argument.
This would be useful in functions that have to call decodeValue
inside an andThen
continuation.
Here's an example of a case where such a function would be nice (I defined a sub-optimal version of error
so it would compile):
https://ellie-app.com/bgcTCxqmSKWa1
Given that the Error type constructors are already exposed from this module, I don't see a good reason not to allow this sort of thing, but maybe there's a downside to this that I haven't considered.
Similar to Bytes.Bytes and Bytes.Encode, Bytes.Decode.
I would be nice to have Json.Json type and remove Encode.Value and Decode.Value.
I was confused what is difference between Encode.Value and Decode.Value.
And then I check the source code and realize that it is alias.
I would say that having Json.Json type will make very clear - it is a JSON!
What do you think?
I wasn't sure if this should be reported in elm/compiler or here, but it is a curious case, and I lost some hours trying to figure out why it won't work and rewriting it until I moved the decoder to top level.
A contrived example can be found at: https://ellie-app.com/bjMK9h2bvDga1
It feels odd that elm/json talks about deaths in its introduction example. Picture a person which just lost one of their relatives.
Alternative ideas:
There are cases when the format of the JSON has "sneaky floats/ints". That is, it contains an int or float inside a string instead of a JSON number.
For example:
{
"foo": "42.0"
}
This is a case where I would want to decode that value to a float. The straightforward solution with the current API is to parse it as a string then convert it to float with String.toFloat
. But that leads to something like:
Decode.field "lat" <|
Decode.map (Maybe.withDefault (0 / 0) << String.toFloat) Decode.string
We need to specify some default value, NaN
in this case, or end up with Decoder (Maybe Float)
. Suboptimal. I want decoding to either fail or result succeed with a Float. A possible solution would be exposing a function, like
stringAs : Decoder value -> Decoder value
Which would allow usage as follows:
Decode.field "lat" <| Decode.stringAs Decode.float
Much nicer, and there is no hidden fail state with a pesky NaN
default value.
While it is possible to define something like that in terms of the current public API, doing so loses information about the error. There is no way to create a failing Decoder out of an Error: #25
The current best implementation is something like this:
stringAs : Decode.Decoder value -> Decode.Decoder value
stringAs decoder =
Decode.string
|> Decode.andThen
(\str ->
case Decode.decodeString decoder str of
Err err ->
-- If this could be just `Decode.error err` then everything would be solved
Decode.fail <| Decode.errorToString err
Ok val ->
Decode.succeed val
)
Please either resolve #25 or add something like stringAs : Decoder a -> Decoder a
that parses a JSON string with a different decoder.
> import Json.Encode as JE
> JE.object [ ("a", JE.list identity []) ] == JE.object []
True : Bool
> JE.object [ ("a", JE.object []) ] == JE.object []
True : Bool
> JE.object [ ("a", JE.null) ] == JE.object [ ( "a", JE.null ), ( "b", JE.null ) ]
True : Bool
Edit: This Discourse post by Evan explains that (==)
shouldn’t be used with JSON values from elm/json and an idea for improving this in the future: https://discourse.elm-lang.org/t/function-equality/7538/24
Edit2: It’s even documented that one shouldn’t use (==)
with JSON values: https://package.elm-lang.org/packages/elm/core/latest/Basics#equality 😅 But who reads the docs for (==)
, right?
Line 477 in cb96b26
I believe this reference should be to elm/json
, even though it remains true that elm/core
can't depend on elm/html
.
The example given for Json.Decode.dict
is failing in elm/json 1.1.0 with error
Problem with the given value: { "alice": 42, "bob": 99 } Expecting an OBJECT
The example (https://ellie-app.com/3T4kj4gHJ5Za1)
decodeString (dict int) "{ \"alice\": 42, \"bob\": 99 }"
Is there a reason the Decode module doesn't expose an andMap? I found myself needing this in an app I'm working on, and ended up just defining it inline:
decodeAndMap = Decode.map2 (\f x -> f x)
...not a big deal to have to do that, but was there a reason for the omission? Thoughts on including it?
At the moment there's a kernel panick
$ elm --version
0.19.0
$ elm-json --version
elm-json 0.2.0
$ RUST_BACKTRACE=1 elm-json install krisajenkins/elm-dialog
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:345:21
stack backtrace:
0: start
1: start
2: start
3: start
4: start
5: start
6: start
7: start
8: start
9: start
10: start
11: start
12: start
13: start
14: main
15: start
16: main
krisajenkins/elm-dialog has never been updated to work with 0.19
When one is passing an object with circular dependencies from JS to Elm and then calls Json.Encode.encode
on the Value
you get a runtime Error from within Elm.
Of course the source of the error is from JS land but it would be nice if Elm gave a better Error message and log something like "<Json with circular references>"
rather than a stack trace. Maybe the JSON.stringify
call could be put in a try/catch or something. It's not a huge issue but I have encountered this error multiple times when people were printing stuff for debugging and it would be helpful if Elm told them the source of their error right away.
SSCCE:
-- Elm
module Main exposing (..)
import Browser
import Html exposing (text)
import Json.Encode as JE
type alias Model =
{}
type Msg
= NoOp
init : JE.Value -> ( Model, Cmd Msg )
init flags =
let _ = JE.encode 2 flags -- will cause an infinite recursion
in
( {}, Cmd.none )
main : Program JE.Value Model Msg
main =
Browser.document
{ view = \_ -> { title="SSCCE", body = [ text "this will crash" ] }
, init = init
, update = \msg model -> (model, Cmd.none)
, subscriptions = always Sub.none
}
// JS
Elm.Main.init({ flags: window });
result = Json.Decode.decodeValue (Json.Decode.dict Json.Decode.string) jsValue
when jsValue
has been created in JavaScript using Object.create(null)
and sent through flags or a port.
Attempting the decoding above results in a runtime exception with this error:
Uncaught TypeError: value.hasOwnProperty is not a function
at _Json_runHelp (VM37 workspace:1461)
at _Json_runHelp (VM37 workspace:1478)
at Function.f (VM37 workspace:1400)
at A2 (VM37 workspace:56)
at $author$project$Main$init (VM37 workspace:10540)
at VM37 workspace:8540
at VM37 workspace:20
at _Platform_initialize (VM37 workspace:1877)
at VM37 workspace:3973
at Object.init (VM37 workspace:20)
I created an Ellie here that you can interact with: https://ellie-app.com/8ZWrTfBzWRZa1
Object.create(null)
is sometimes used to create a JavaScript dictionary . The difference with {}
is that {}
contains fields from the Object prototype, where Object.create(null)
has no prototype and no fields at all.
> ({}).toString
[Function: toString]
> Object.create(null).toString
undefined
This distinction, at least in JavaScript, can be important when you use this value as a set and only care about whether the field exists.
This distinction is irrelevant when decoding, but this runtime error makes it impossible to send values that in JS-land needed to be created this way without somehow cloning them beforehand.
Hello!
I'm working with dictionaries of character-based keys. Encoding them is straight-forward, as I simply have to convert keys from characters to strings. No problem!
Writing the decoder for this kind of dictionary was more problematic, as I don't have the option convert keys back into characters.
Encode.dict : (k -> String) -> (v -> Encode.Value) -> Dict k v -> Encode.Value
Decode.dict : Decoder a -> Decoder (Dict String a)
I am working around this, but I'd love for the solution to be incorporated into Decode.dict
, to avoid the additional passes through the data.
I'd really like these functions to be symmetrical, in the sense that the dictionary-decoder takes a function to convert the string-key into a comparable value in the same way the encoder requires me to convert the comparable keys into strings.
Perhaps like this:
Decode.dict : (String -> comparable) -> Decoder a -> Decoder (Dict comparable a)
To spare you the time, I could do this implementation for you, but let me know what you think when you have the time ☀️
I recently needed to go beyond map8, and the most elegant way I found of doing it was to define the following:
decoderChain : Json.Decode.Decoder a -> Json.Decode.Decoder (a -> b) -> Json.Decode.Decoder b
decoderChain =
Json.Decode.map2 (|>)
Then, instead of writing
decoder : D.Decoder Cause
decoder =
D.map3 Cause
(D.field "name" D.string)
(D.field "percent" D.float)
(D.field "per100k" D.float)
we can write
decoder : D.Decoder Cause
decoder =
Json.Decoder.succeed Cause
|> decoderChain (D.field "name" D.string)
|> decoderChain (D.field "percent" D.float)
|> decoderChain (D.field "per100k" D.float)
This will extend to an arbitrary number of arguments, also allowing adding and removing arguments without needing to update the number.
This strikes me as something that may be useful included within Json.Decode.
I was looking at the code which tests JSON decoders for equality, and thought there might be an improvement available to the code quoted below.
The case I'm thinking about is the _1_SUCCEED
case, where the msg
may be any Elm value.
The quoted code is testing for equality using Javascript's ===
. Unless I'm missing something, it would seem better to use Basics.eq
here, since it will handle some Elm values more accurately than Javascript's ===
will.
Of course, there may be any number of good reasons why you're sticking to ===
here.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.