aliko aliko - 4 months ago 36
JSON Question

map over json data with haskell

I'm just learning a haskell and seems like all is good even scary monads are not a big deal for me. But I can't get to real practiacal stuff at all.

My first practical task for haskell I choosed as follows:
Given a JSON describing some binary file's format to parse that file.

JSON has some deeply nested structure with lists of assocoative lists (dictionaries) of lists etc with endpoints as numbers or strings.
So first of all I want to be able to map other those endpoints (to have functor class for jsons data) converting some strings to numbers in particular. Also it would be nice to be able to fold all those endpoints as well.

I came up with some python code easily. But can't go any far with haskell.
So what your suggestions for implementing things in haskell? It really would be nice to hear some advise for solutions using libraries to greatest extent and not handwrite all the stuff from scratch.

Thanks in advance!

example of what I have in python

Some helper functions:

islist = lambda l: isinstance(l, collections.Iterable) and not isinstance(l, (str, bytes))
isdict = lambda d: isinstance(d, collections.Mapping)
isiter = lambda i: islist(i) or isdict(i)

def iterable(d):
if isdict(d):
i = d.items()
elif islist(d):
i = enumerate(d)
raise ValueError
return i

Iterator over nested json data:

def nested_iter(nested, f = lambda *args: None):
for key, value in iterable(nested):
if not isiter(value):
f(nested, key)
yield key, value
yield from nested_iter(value, f)

now I can substitute some numbers with lists of keys:

def list_from_num(d, k):
if type(d[k]) == int:
d[k] = [k]*d[k]
list(nested_iter(typedef, list_from_num))

or I can substitute some strings with some other nested data with the same key name

def nest_dicts(defs, d, k):
if d[k] in defs.keys():
d[k] = deepcopy(defs[d[k]])
if isiter(d[k]):
list(nested_iter(d[k], partial(nest_dicts, defs)))
list(nested_iter(typedef, partial(nest_dicts, typedef)))

or can just flatten data


parsing binary is a bit more evolved but it is nothing more as passing to iterator one more function


Well this is my solution. It uses Control.Lens, Data.Aeson.Lens, Control.Lens.Plated

One can use transform from Uniplate or Lens.Plated to transform values.

for example to substitute each number with list of key values of length of that number:

n2k :: T.Text -> Value -> Value --import qualified Data.Text as T
n2k s (Number x)
  | isInteger x = case toBoundedInteger x of
    Just n    -> Array (V.replicate n (String s)) -- import qualified Data.Vector as V
    _         -> Number x
  | otherwise =  Number x
n2k _ v = v

f (Object o) = Object $ imap n2k o --imap from Data.Map.Lens
f x          = x

j2 = transform f j --transform JSON j using function f

to substitute string with data with same key:

-- o is hashmap where we are looking for keys to substitute strings
h (String s) = fromMaybe (String s) (H.lookup s o) --import qualified Data.HashMap.Lazy as H
h x          = x

j2 = transform h j 

just get all numbers into list:

l = [x | Number x <- universe j]