4

I have a list of aeson objects like this

[object ["key1" .= "value1"], object ["key2" .= "value2"]] 

and I want to merge them as a single aeson object like this

object ["key1" .= "value1", "key2" .= "value2"]

This is quite standard when working with JSON data in other languages (merge operation) but I don't see anything similar in the Aeson library.

Am I just missing something and can this be done with some standard haskell function? I tried using sequence but it seems that JSON Value is not a monad so I cannot do that.

I don't need to deal with deep merging or duplicate keys, I just want to generate something like

{
  "key1": value1,
  "key2": value2
}

from

[{ "key1": value1 }, { "key2": value2 }]
Batou99
  • 869
  • 10
  • 19
  • If you have objects with types the `Object` then it's just `HashMap`, so you can use `HashMap.union`, `mappend`, `(<>)` etc. But if you have objects with types the `Value`, how do you want to merge them? For example, you have a string and a number, what result must be? – freestyle Jun 07 '17 at 09:44
  • I just want the `unions` behavior. I want a Map with all keys and their values and if some key is duplicated I'm happy to let the last one overwrite the others. Essentially the same behaviour you get on ruby when doing `hash1.merge(hash2)` – Batou99 Jun 08 '17 at 09:58

1 Answers1

8

Given the list contains only of JSON objects (thus elements that have key-value pairs or elements with the Object constructor), you can write your own:

import Data.Aeson(Value(Object))
import qualified Data.HashMap.Lazy as HML

merge_aeson :: [Value] -> Value
merge_aeson = Object . HML.unions . map (\(Object x) -> x)

If we test the above function with the given sample input, we obtain:

Prelude Data.Aeson HML> merge_aeson [object ["key1" .= "value1"], object ["key2" .= "value2"]] 
Object (fromList [("key2",String "value2"),("key1",String "value1")])
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • The function retains the first (earliest) value in case of the same key in more than one of the merged records. First Wins strategy comes from the `Data.HashMap.Lazy`. – user855443 Jun 24 '20 at 13:40
  • @user855443: you can make use of `foldl (unionsWith ...) empty` to specify a different strategy: https://hackage.haskell.org/package/unordered-containers-0.2.11.0/docs/Data-HashMap-Lazy.html#v:unionWith you can for example use `const` to select the first (earliest) one, or `const id` to select the latter (latest). Or you can even implement a more sophisticated strategy – Willem Van Onsem Jun 24 '20 at 13:47