Search code examples
jsondecodeelm

Decoding Dict in Elm failing due to extra backslashes


I'm trying to send a dict to javascript via port for storing the value in localStorage, and retrieve it next time the Elm app starts via flag.

Below code snippets show the dict sent as well as the raw json value received through flag. The Json decoding fails showing the error message at the bottom.

The issue seems to be the extra backslashes (as in \"{\\"Left\\") contained in the raw flag value. Interestingly, console.log shows that the flag value passed by javascript is "dict1:{"Left":"fullHeightVerticalCenter","Right":"fullHeightVerticalCenter","_default":"fullHeightVerticalBottom"}"as intended, so the extra backslashes seem to be added by Elm, but I can't figure out why. Also, I'd be interested to find out a better way to achieve passing a dict to and from javascript.

import Json.Decode as JD
import Json.Encode as JE

dict1 = Dict.fromList[("_default", "fullHeightVerticalBottom")
                        , ("Left", "fullHeightVerticalCenter")
                        , ("Right", "fullHeightVerticalCenter")]

type alias FlagsJEValue =
   {dict1: String}

port setStorage : FlagsJEValue -> Cmd msg

-- inside Update function Cmd
setStorage {dict1 = JE.encode 0 (dictEncoder JE.string model.dict1)}

dictEncoder enc dict = 
   Dict.toList dict 
     |> List.map (\(k,v) -> (k, enc v)) 
     |> JE.object 
-- 
type alias Flags =
   {dict1: Dict String String} 

flagsDecoder : Decoder Flags
flagsDecoder =
    JD.succeed Flags
     |> required "dict1" (JD.dict JD.string)

-- inside `init`
case JD.decodeValue MyDecoders.flagsDecoder raw_flags of
   Err e ->
      _ = Debug.log "raw flag value" (Debug.toString (JE.encode 2 raw_flags) )
      _ = Debug.log "flags error msg" (Debug.toString e)

      ... omitted ...

   Ok flags ->
      ... omitted ...


-- raw flag value
"{\n  \"dict1\": \"{\\\"Left\\\":\\\"fullHeightVerticalCenter\\\",\\\"Right\\\":\\\"fullHeightVerticalCenter\\\",\\\"_default\\\":\\\"fullHeightVerticalBottom\\\"}\"\n}"

 --flags error msg

"Failure \"Json.Decode.oneOf failed in the following 2 ways:\\n\\n\\n\\n
(1) Problem with the given value:\\n    \\n    \\\"{\\\\\\\"Left\\\\\\\":\\\\\\\"fullHeightVerticalCenter\\\\\\\",\\\\\\\"Right\\\\\\\":\\\\\\\"fullHeightVerticalCenter\\\\\\\",\\\\\\\"_default\\\\\\\":\\\\\\\"fullHeightVerticalBottom\\\\\\\"}\\\"\\n    \\n    Expecting an OBJECT\\n\\n\\n\\n

(2) Problem with the given value:\\n    \\n    \\\"{\\\\\\\"Left\\\\\\\":\\\\\\\"fullHeightVerticalCenter\\\\\\\",\\\\\\\"Right\\\\\\\":\\\\\\\"fullHeightVerticalCenter\\\\\\\",\\\\\\\"_default\\\\\\\":\\\\\\\"fullHeightVerticalBottom\\\\\\\"}\\\"\\n    \\n    Expecting null\" <internals>”

Solution

  • You don't need to use JE.encode there.

    You can just use your dictEncoder to produce a Json.Encode.Value and pass that directly to setStorage.

    The problem you're encountering it that you've encoded the dict to a json string (using JE.encode) and then sent that string over a port and the port has encoded that string as json again. You see extra slashes because the json string is double encoded.