Finding a value in ByteString (which is actually JSON) - json

A web service returns a response as ByteString
req <- parseUrl "https://api.example.com"
res <- withManager $ httpLbs $ configReq req
case (HashMap.lookup "result" $ responseBody res) of .... -- error - responseBody returns ByteString
where
configReq r = --......
To be more specific, responseBody returns data in ByteString, although it's actually valid JSON. I need to find a value in it. Obviously, it would be easier to find it if it was JSON and not ByteString.
If that's the case, how do I convert it to JSON?
UPDATE:
decode $ responseBody resp :: IO (Either String Aeson.Value)
error:
Couldn't match expected type `IO (Either String Value)'
with actual type `Maybe a0'

You'll find several resources for converting bytestring to JSON. The simplest use cases are on the hackage page itself, and the rest you can infer using type signatures of the entities involved.
https://hackage.haskell.org/package/aeson-0.7.0.6/docs/Data-Aeson.html
But here's a super quick intro to JSON with Aeson:
In most languages, you have things like this:
someString = '{ "name" : ["value1", 2] }'
aDict = json.loads(someString)
This is obviously great, because JSON has a nearly one to one mapping with a fundamental data-structure of the language. Containers in most dynamic languages can contain values of any type, and so moving from JSON to data structure is a single step.
However, that is not the case with Haskell. You can't put things of arbitrary types into a container like type (A list, or a dictionary).
So Aeson does a neat thing. It defines an intermediate Haskell type for you, that maps directly to JSON.
A fundamental unit in Aeson is a Value. The Value can contain many things. Like an integer, string, an array, or an object.
https://hackage.haskell.org/package/aeson-0.7.0.6/docs/Data-Aeson.html#t:Value
An aeson array is a Vector (like a list but better) of Values and an aeson object is a HashMap of Text to Values
The next interesting step is that you can define functions that will convert an Aeson value to your Haskell type. This completes the loop. ByteString to Value to a custom type.
So all you do is implement parseJSON and toJSON functions that convert aeson Values to your type and vice-versa. The bit that converts a bytestring into a valid aeson value is implemented by aeson. So the heavy lifting is all done.
Just important to note, that Aeson bytestring is a lazy bytestring, so you might need some strict to lazy helpers.
stringToLazy :: String -> ByteString
stringToLazy x = Data.Bytestring.Lazy.fromChunks [(Data.ByteString.Char8.pack x)]
lazyToString :: ByteString -> String
lazyToString x = Data.ByteString.Char8.unpack $ Data.ByteString.Char8.concat $ Data.ByteString.Lazy.toChunks
That should be enough to get started with Aeson.
--
Common decoding functions with Aeson:
decode :: ByteString -> Maybe YourType
eitherDecode :: ByteString -> Either String YourType.
In your case, you're looking for eitherDecode.

Related

How can I access JSON values from GHCI?

I'm trying to navigate JSON values using Haskell, in GHCI. I can get a JSON payload from an API, with something like this:
import Network.HTTP.Simple
baseURL <- parseRequest "https://www.googleapis.com/books/v1/volumes"
let queryString = B8.pack $ unpack q
let request = setRequestQueryString [("q", Just queryString)] $ baseURL
resp <- httpJSON request
let body = getResponseBody resp :: Object
And that gives me an Object. That object (a HashMap) contains the key "items" whose value is an Array of Objects. I want to get the first object from taht array, then gets its volumeInfo, then its industryIdentifiers, then its isbn.
In Python I would do:
identifiers = body['items'][0]['industryIdentifiers']
isbn = [id['itentifier'] for id in identifiers if id['type'] == 'ISBN_10'][0]
Or in other words, just chain accessors. How can I do this in Haskell? I've tried something like ((body ! "items") !! 0) ! "volumeInfo") but I keep getting errors like Couldn't match expected type ‘[a]’ with actual type ‘Value’.
All the tutorials I can find just say to model the data by creating a complete picture of the data as a Haskell data structure, then writing a decoder to turn that JSON data into a Haskell data object. That seems like massive overkill in this case, when the data structure I'm getting from the API is way bigger than the bit that I need, which is just the ISBN.
How does one normally drill down through a big data structure in Haskell?
The preferred way is to not manually deal with JSON values, but instead parse them into a suitable Haskell type and then index into that, which is much safer: if the input doesn't conform to the expected format, you get a clear parsing error show up at which location in the data structure something is missing, instead of an obscure key-missing error somewhere deep in your code.
{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
data GoogleBooksVolumes = GoogleBooksVolumes
{ items :: Array GoogleBooksVolume
, ...
} deriving (Generic, FromJSON, ToJSON)
data GoogleBooksVolume = GoogleBooksVolume
{ ...
, industryIdentifiers :: Array IndustryIdentifier
, ...
} deriving (Generic, FromJSON, ToJSON)
...
If you're going to ad-hoc index into the JSON object, your best bet is the aeson-lens package. That allows you to do something very similar – and similarly unsafe – as in Python.
{-# LANGUAGE OverloadedStrings #-}
Just identifiers = Just body ^. key "items" . nth 0 . key "industryIdentifiers"
Just isbn = head [ Just idf ^. key "identifier"
| idf <- identifiers
, Just idf ^. key "type" == Just (String "ISBN_10") ]
TBH this is even worse than in Python, because if a key fails to match you just get a Nothing result without any information at all what went wrong.
A safer option is to manually pattern-match at every decision where something could go wrong, but that is a lot of boilerplate.

Haskell Data.Decimal as Aeson type

Is it possible to parse a Data.Decimal from JSON using the Aeson package?
Suppose I have the following JSON:
{
"foo": 5.231,
"bar": "smth"
}
And the following record type:
data test { foo :: Data.Decimal
, bar :: String } deriving Generic
with
instance FromJSON test
instance ToJSON test
This would work, if it weren't for the Data.Decimal value "foo".
From what I understand I would need to manually create a FromJSON and ToJSON (for converting back to JSON) instance of Data.Decimal, since it doesn't derive from Generic. How can I do that?
Thanks in advance.
I know this is an old thread, but might help someone. Here is how I am converting Decimal to/from json (I assembled this code from a bunch of other code which I don't have the source for now):
instance A.ToJSON (DecimalRaw Integer) where
-- maybe this is not the best, but it works
toJSON d = A.toJSON $ show d
instance A.FromJSON (DecimalRaw Integer) where
parseJSON = A.withText "Decimal" (go . (read :: String -> [(DecimalRaw Integer, String)]) . T.unpack)
where
go [(v, [])] = return v
go (_ : xs) = go xs
go _ = fail "Could not parse number"

How to use Haskell "json" package to parse to type [Map String String]?

I've got some sample JSON data like this:
[{
"File:FileSize": "104 MB",
"File:FileModifyDate": "2015:04:11 10:39:00-07:00",
"File:FileAccessDate": "2016:01:17 22:37:23-08:00",
"File:FileInodeChangeDate": "2015:04:26 07:50:50-07:00"
}]
and I'm trying to parse the data using the json package (not aeson):
import qualified Data.Map.Lazy as M
import Text.JSON
content <- readFile "file.txt"
decode content :: Result [M.Map String String]
This gives me an error:
Error "readJSON{Map}: unable to parse array value"
I can get as far as this:
fmap
(map (M.fromList . fromJSObject))
(decode content :: Result [JSObject String])
but it seems like an awfully manual way to do it. Surely the JSON data could be parsed directly into a type [Map String String]. Pointers?
Without MAP_AS_DICT switch, the JSON (MAP a b) instance will be:
instance (Ord a, JSON a, JSON b) => JSON (M.Map a b) where
showJSON = encJSArray M.toList
readJSON = decJSArray "Map" M.fromList
So only JSON array can be parsed to Data.Map, otherwise it will call mkError and terminate.
Due to haskell's restriction on instances, you won't be able to write an instance for JSON (Map a b) yourself, so your current workaround may be the best solution.

scala lift json: pattern match on unknown data?

I have some strange json that I cannot change, and I wish to parse it using
the JsonParsen in lift.
A typical json is like:
{"name":"xxx", "data":{
"data_123456":{"id":"Hello"},
"data_789901":{"id":"Hello"},
"data_987654":{"id":"Hello"},
}}
The issue is that the keys for the data are unknown (data_xxxxx, where the xx:s
are not known).
This is bad json, but I have to live with it.
How am I supposed to setup case-classes in scala to be able to build a proper
structure when the keys here are unknown, but the structure is known?
You can use a Map, and every value can be JValue too, representing unparsed JSON. Example:
case class Id(id: String)
case class Data(name: JValue, data: Map[String, Id])
And then:
json.extract[Data]
res0: Data(JString(xxx),Map(data_123456 -> Id(Hello), data_789901 -> Id(Hello), data_987654 -> Id(Hello)))

How do I use the json library?

I'm trying to figure out Haskell's json library. However, I'm running into a bit of an issue in ghci:
Prelude> import Text.JSON
Prelude Text.JSON> decode "[1,2,3]"
<interactive>:1:0:
Ambiguous type variable `a' in the constraint:
`JSON a' arising from a use of `decode' at <interactive>:1:0-15
Probable fix: add a type signature that fixes these type variable(s)
I think this has something to do with the a in the type signature:
decode :: JSON a => String -> Result a
Can someone show me:
How to decode a string?
What's going with the type system here?
You need to specify which type you want to get back, like this:
decode "[1,2,3]" :: Result [Integer]
-- Ok [1,2,3]
If that line was part of a larger program where you would go on and use the result of decode the type could just be inferred, but since ghci doesn't know which type you need, it can't infer it.
It's the same reason why read "[1,2,3]" doesn't work without a type annotation or more context.
The decode function is defined as follows:
decode :: JSON a => String -> Result a
In a real program, the type inference engine can usually figure out what type to expect from decode. For example:
userAge :: String -> Int
userAge input = case decode input of
Result a -> a
_ -> error $ "Couldn't parse " ++ input
In this case, the type of userAge causes the typechecker to infer that decode's return value, in this particular case, is Result Int.
However, when you use decode in GHCi, you must specify the type of the value, e.g.:
decode "6" :: Result Int
=> Ok 6
A quick glance at the docs seems to suggest that the purpose of this function is to allow you to read JSON into any Haskell data structure of a supported type, so
decode "[1, 2, 3]" :: Result [Int]
ought to work