I am doing a http request with elm and my response is an Array of Objects. Each object is as follows
obj = {
title: "Some Title",
words: [ "word1", "word2", "word3", "word4" ]
}
Here is my decoder so far:
type alias ThisRes = List ResObj
type alias ResObj =
title: String
words: List String
decoded : Decoder ThisRes
decoded =
decode ThisRes
I can't seem to get the decoder right and any help that can be provided would be appreciated.
obj =
"""
{
"title": "Some Title",
"words": [ "word1", "word2", "word3", "word4" ]
}
"""
type alias ResObj =
{ title : String, words : List String }
objDecoder =
map2 ResObj
(at [ "title" ] string)
(at [ "words" ] (list string))
headingFrom : Result String ResObj -> String
headingFrom result =
case result of
Ok resobj ->
resobj.title
Err reason ->
toString reason
main =
h1 [] [ text <| headingFrom <| decodeString objDecoder obj ]
Breaking this down:
obj is just a string representing some JSON, to illustrate the example.
You define the ResObj type alias, and get a type constructor for it. Now you can create ResObj values by calling ResObj "MyTitle" ["word1", "wordb", "wordFightingMongooses"].
objDecoder is a value that helps decode JSON into ResObj. It calls map2, which takes a type constructor, and then two decoded JSON fields. (at ["title"] string) says "turn the value at title in the JSON into a string" and you can guess what the next line does. So this evaluates to, ultimately, ResObj "SomeTitle" ["word1", "word2", "word3", "word4"] and creates your value.
Down in our main, we have the expression decodeString objDecoder obj. decodeString takes a decoder value, and a JSON string, and sees if it can decode it. But it doesn't return the ResObj itself -- because the decoding can fail, it returns a Result.
So we have to process that Result with our function headingFrom. If the decoding succeeded our result will be Ok resobj, and we can work with the resobj finally. If it failed it'll be Err reason.
Related
I'm doing a server api using GoLang, trying to manage and answer requests made. Using net/http and github.com/gorilla/mux.
When getting a request, I was using the following structure to create a response:
type Response struct {
Error bool `json:"error"`
Msg string `json:"msg,omitempty"`
Data string `json:"data,omitempty"`
AccessToken string `json:"access_token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
}
When retrieving several message type (another struct) from the Database I get a type []primitive.M. My question is how can I transform the []primitive.M to a string to pass it as a data field? I tried to use msgsStr := Sprintf("%s", msgs) but the response seemed to come out of format.
I'm assuming using the string type for Data field is correct since the objective is to pass json type information.
Partial example of result passing the []primitive.M directly:
[
{
"_id": "xxxxx",
"datetime": {
"T": 1589193727,
"I": 1
},
"dislikes": "4",
"latitude": 21.5,
"likes": "32",
"longitude": 12.2,
"text": "message 123!",
"user": "user#123121"
},
{
"_id": "xxxxxx",
"datetime": {
"T": 1589193727,
"I": 1
},
"dislikes": "4",
"latitude": 14.4,
"likes": "32",
"longitude": 15,
"text": "hello! 22",
"user": "user#123121"
},
After changing with Sprintf:
[map[_id:ObjectID(\"xxxxx\") datetime:{%!s(uint32=1589193727) %!s(uint32=1)} dislikes:4 latitude:%!s(float64=21.5) likes:32 longitude:%!s(float64=12.2) text:message 123! user:user#123121] map[_id:ObjectID(\"xxxxxx\") datetime:{%!s(uint32=1589193727) %!s(uint32=1)} dislikes:4 latitude:%!s(float64=14.4) likes:32 longitude:%!s(int32=15) text:hello! 22 user:user#123121]]
So if I understand your question correctly, you're trying to set the Data field on your Response type to a string, which is also valid JSON. That's easy enough, but will require some changes:
type Response struct {
Error bool `json:"error"`
Msg *string `json:"msg,omitempty"`
Data *json.RawMessage `json:"data,omitempty"`
AccessToken *string `json:"access_token,omitempty"`
RefreshToken *string `json:"refresh_token,omitempty"`
}
As you can see, I've changed most of your fields to pointers. This is because the omitempty tag doesn't really make sense unless the field is a pointer. An explicitly set empty string is not going to be omitted. The field has a value. Think of fields that are int, for example. Is a 0 value a value to omit? What if someone actually wants to pass in the value 0? Pointer fields allow you to distinguish between missing/omitted fields, and nil values:
var resp Response
if err := json.Unmarshal(&resp, data); err != nil {
// handle error
}
if resp.Msg == nil {
// msg was missing -> omitted
}
if *resp.Msg == "" {
// Msg was set, but its value is an empty string
}
Anyway, let's set the Data field to be a valid JSON string. We use the type json.RawMessage to indicate that, when marshalling the entire Response struct, the contents of that field has to be preserved as-is. If not, JSON characters are going to be escaped, and the result will not be valid json:
prim, err := json.Marshal(msgs)
if err != nil {
// handle error
}
resp.Data = prim // json.RawMessage underneath is a []byte
I'm currently learning elm, I just stumbled on this problem where the div returns a Html (String -> Msg) instead of Html Msg.
error message I'm receiving
This div call produces:
Html (String -> Msg)
But the type annotation on view says it should be:
Html Msg
type alias Model =
{
firstNum: String,
secondNum: String,
answer: String
}
init: Model
init = { firstNum = "",
secondNum = "",
answer = ""}
type Msg =
Add String| Minus String
update: Msg -> Model -> Model
update msg model =
case msg of
Add x -> { model | answer = x}
Minus y -> { model | answer = y}
view : Model -> Html Msg
view model =
div []
[
input [ placeholder "Text to reverse", value model.firstNum] [],
button [onClick Add] [text "add"],
div [] [text model.answer]
]
main =
Browser.sandbox
{ init = init,
update = update,
view = view
}
You define the Msg type as
type Msg =
Add String| Minus String
with Add taking a String argument, but when you use it here:
button [onClick Add] [text "add"],
you're not giving it any argument at all.
The underlying issue seems to be that your mental model of the Elm Architecture is wrong. You seem to consider messages as "operations" or function calls rather than events, where Add is a function that takes an argument to apply to the model.
You should instead consider a message as a description of what triggered it. Instead of Add String, you might call it AddButtonClicked, with no arguments (in this case). Then have the update function do what it should based on what's in the model alone, which I'm guessing is an arithmetic operation on firstNum and secondNum.
But you're also not populating those fields. To do so you need to use the onInput event, which does ask for a message that takes a String. You might add a new message FirstNumChanged String for example, then use it with input like this:
input [ placeholder "Text to reverse", onInput FirstNumChanged, value model.firstNum] [],
I'll leave it to you to figure out how to handle it in update.
I am experimenting with Elm based on their tutorial, and is encountering a problem with function argument declaration.
Basically I just extracted a function within the tutorial code. It works fine without function declaration, but fails when I include it.
The essence of the code is:
type Msg
= Name String
| Password String
view : Model -> Html Msg
view model =
div []
[ myInput "text" "Name" Name
]
myInput : String -> String -> Msg -> Html Msg
myInput type__ label handle =
input [ type_ type__, placeholder label, onInput Name ] []
And the error message is:
The 3rd argument to function myInput is causing a mismatch.
47| myInput "text" "Name" Name
Function myInput is expecting the 3rd argument to be:
Msg
But it is:
String -> Msg
Hint: It looks like a function needs 1 more argument.
Ideally I would also like the argument to onInput be the argument called "handle", declared in myInput.
The type signature for myInput is incorrect. The constructor Name has a single argument, which means when used as a function its signature is (String -> Msg). That is what you should use for the annotation of the handle parameter.
myInput : String -> String -> (String -> Msg) -> Html Msg
myInput type__ label handle =
input [ type_ type__, placeholder label, onInput handle ] []
I'm receiving a JSON that looks like this:
{ name: "NAME1", value: "true" }
I would like to create a json decoder that would create a record like this:
{ name: "NAME1", value: True }
I'm trying to make a decoder that transforms "true" into True. I did this so far:
userProperties : Json.Decode.Decoder Props
userProperties =
Json.Decode.object2 (,)
("name" := Json.Decode.string)
("value" := Json.Decode.string)
`andThen` \val ->
let
newVal = -- Do something here?
in
Json.Decode.succeed <| newVal
There are a few problems in your example so let's step through each of them.
You haven't shown the definition of Props so I'm assuming, based on your example, that it is this:
type alias Props = { name : String, value : Bool }
You are passing (,) as the first argument to object2 which indicates that you will be returning a Decoder of type tuple. That should probably be:
Json.Decode.object2 Props
Now, the way you are using andThen won't compile because of its order of precedence. If you were to parenthesize the whole thing, it would look like this:
userProperties =
(Json.Decode.object2 Props
("name" := Json.Decode.string)
("value" := Json.Decode.string))
`andThen` \val ->
let
newVal = -- Do something here?
in
Json.Decode.succeed <| newVal
That isn't going to be correct since the thing you want to andThen is the string "true" in the "value" field. To do that, I would recommend creating a decoder which provides that boolean decoder:
stringBoolDecoder : Json.Decode.Decoder Bool
stringBoolDecoder =
string `andThen` \val ->
case val of
"true" -> succeed True
"false" -> succeed False
_ -> fail <| "Expecting \"true\" or \"false\" but found " ++ val
I'm only guessing at the handling of "false" and the catch-all underscore. Change their implementation as suits your business case.
When building complex decoders, it is often best to break up decoder definitions into the smallest chunk possible like in the above.
Lastly, we can now redefine your userProperties decoder to use the stringBoolDecoder in the appropriate place:
userProperties : Json.Decode.Decoder Props
userProperties =
Json.Decode.object2 Props
("name" := Json.Decode.string)
("value" := stringBoolDecoder)
The canonical example for getting the value from an input is:
view : Address String -> String -> Html
view address string =
div []
[ input
[ placeholder "Text to reverse"
, value string
, on "input" targetValue (Signal.message address)
, myStyle
]
[]
, div [ myStyle ] [ text (String.reverse string) ]
]
I get this. But I want my address to be of type Address String Action (where Action is some other type I define). To my understanding, this would mean the address expects a String followed by a Action type as it's "arguments" (I think of Address as a function, but that might not be correct).
Is it possible to use an address type of Address String Action, and then use it with an input in a similar way? Or am I allowed to do Address String Action in the first place?
The example you link to is probably a bit too simplistic in that both the Action and Model are a string. You will seldom run into that.
I've tweaked the example with something that is more canonical to elm in its current form:
main =
StartApp.start { model = { text = "" }, view = view, update = update }
type Action
= SetText String
type alias Model =
{ text : String }
update : Action -> Model -> Model
update action model =
case action of
SetText text ->
{ model | text = text }
view : Address Action -> Model -> Html
view address model =
div []
[ input
[ placeholder "Text to reverse"
, value model.text
, on "input" targetValue (Signal.message address << SetText)
, myStyle
]
[]
, div [ myStyle ] [ text (String.reverse model.text) ]
]
Notice how the Action type is a union type listing all the different ways you can interact with the page. In this example, the only thing you can do is to set the text.
The signature of view is now more explicit. The first argument is the address of a mailbox that deals in type Action, and the second argument contains the current state of the model.
view : Address Action -> Model -> Html
There is no need to go down a path of trying something like Address String Action since now Action encapsulates the setting of the text.