Saurabh Nanda
Saurabh Nanda

Reputation: 6793

How to manipulate Json.Encode.Value in Elm?

I'm writing some code to auto-gen JSON codecs for Elm data-structures. There is a point my code, where a "sub-structure/sub-type", has already been encoded to a Json.Encode.Value, and I need to add another key-value pair to it. Is there any way to "destructure" a Json.Encode.Value in Elm? Or combine two values of type Json.Encode.Value?

Here's some sample code:

type alias Entity record =
   { entityKey: (Key record)
   , entityVal: record
   }

jsonEncEntity : (record -> Value) -> Entity record -> Value
jsonEncEntity localEncoder_record val =
  let 
      encodedRecord = localEncoder_record val.entityVal
  in
      -- NOTE: the following line won't compile, but this is essentially
      -- what I'm looking for
      Json.combine encodedRecord (Json.Encode.object [ ( "id", jsonEncKey val.entityKey ) ] )

Upvotes: 4

Views: 1471

Answers (2)

Dogbert
Dogbert

Reputation: 222288

You can decode the value into a list of key value pairs using D.keyValuePairs D.value and then append the new field. Here's how you'd do that:

module Main exposing (..)

import Json.Decode as D
import Json.Encode as E exposing (Value)


addKeyValue : String -> Value -> Value -> Value
addKeyValue key value input =
    case D.decodeValue (D.keyValuePairs D.value) input of
        Ok ok ->
            E.object <| ( key, value ) :: ok

        Err _ ->
            input
> import Main
> import Json.Encode as E
> value = E.object [("a", E.int 1)]
{ a = 1 } : Json.Encode.Value
> value2 = Main.addKeyValue "b" E.null value
{ b = null, a = 1 } : Json.Encode.Value

If the input is not an object, this will return the input unchanged:

> Main.addKeyValue "b" E.null (E.int 1)
1 : Json.Encode.Value

Upvotes: 3

jacobm
jacobm

Reputation: 14035

If you want to do this, you need to use a decoder to unwrap the values by one level into a Dict String Value, then combine the dictionaries, and finally re-encode as a JSON value. You can unwrap like so:

unwrapObject : Value -> Result String (Dict String Value)
unwrapObject value =
    Json.Decode.decodeValue (Json.Decode.dict Json.Decode.value) value

Notice that you have to work with Results from this point on because there's the possibility, as far as Elm is concerned, that your JSON value wasn't really an object (maybe it was a number or a string instead, for instance) and you have to handle that case. For that reason, it's not really best practice to do too much with JSON Values directly; if you can, keep things as Dicts or some other more informative type until the end of processing and then convert the whole result into a Value as the last step.

Upvotes: 1

Related Questions