Search code examples
jsonhaskellhaskell-lensaeson

How can I rewrite an arbitrarily nested field in a JSON structure using haskell lenses?


I would like to make the following test pass:

it "can rewrite a field from an object" $ do
  let _42 = 42 :: Int
      nested =  object [ "foo" .= _42, "baz" .= object [ "bar" .= _42 ] ]
                                           ]
  rewrite "bar" nested  `shouldBe` object [ "foo" .= _42
                                          , "baz" .= object [ "bar" .= ("XXXXXXXX" :: Text) ]
                                          ]

It seems that what I want is something from Control.Lens.Plated like rewrite like or transform but I cannot manage to make it work, probably due to missing deep understanding of lenses.

I managed to change a single layer using the following code but would like to leverage that to nested fields.

 rewrite' field value = value & key field %~ const "XXXXXXXX"

Solution

  • One approach is to use transformOn along with deep to get precise control over the where the transformation is applied.

    ghci> let nested = object [ "foo" .= 42, "baz" .= object [ "bar" .= 42 ] ]
    ghci> transformOn (deep $ key "bar") (const "XXXXXXXX") nested
    Object (fromList [("foo",Number 42.0),("baz",Object (fromList [("bar",String "XXXXXXXX")]))])
    

    deep searches a Plated structure for locations where the provided Traversal matches, and transformOn applies a change to each of its targets.

    EDIT:

    I just realized transformOn is massive overkill for this. You can get away with just deep and simpler lens combinators:

    ghci> deep (key "bar") %~ const "XXXXXXXX" $ nested
    Object (fromList [("foo",Number 42.0),("baz",Object (fromList [("bar",String "XXXXXXXX")]))])
    

    or even

    ghci> deep (key "bar") .~ "XXXXXXXX" $ nested
    Object (fromList [("foo",Number 42.0),("baz",Object (fromList [("bar",String "XXXXXXXX")]))])
    

    The transform* family of functions applies its modifications recursively, which is overkill for the modification you want to make.