0

I recently started playing around with Haskell and can't seem to figure this one out.

I am trying to read from a YAML file that has this format:

id: 1
kind: good
created_at: !ruby/object:ActiveSupport::TimeWithZone
    utc: &1 2017-01-01 10:34:12.533704000 Z
    zone: &2 !ruby/object:ActiveSupport::TimeZone
    time: *2

So far I can read the id and kind perfectly fine by doing this:

data Item= Item{
    id :: Integer,
    kind::String
    }

And

instance FromJSON Item where
    parseJSON(Object v) = Item <$>
        v .: "id" <*>
        v .: "kind"

I am using the package "Data.Yaml" and this function:
let items = Data.Yaml.decode ymlData :: Maybe[Item]

But I can't seem to figure out how to get the timestamp.

What would be the proper way of getting the timestamp, only the "utc" is needed.

Anthon
  • 69,918
  • 32
  • 186
  • 246
  • 1
    The first issue I see is that you have to capitalize your types and constructor, so `data Item = Item ...` and `instance FromJSON Item where ...`. I think your question is really about how to access nested elements, is that what you really want to ask? This problem doesn't seem to really be about getting timestamps out, but rather about how to work with nested structures in YAML. – bheklilr Mar 02 '17 at 15:57
  • @bheklilr Thank you for your reponse, yes you are right. The question should indeed be: "how to access nested elements?" – QuickQuestionGuy Mar 02 '17 at 16:35
  • Are you using [the *yaml* package](https://hackage.haskell.org/package/yaml-0.8.9/docs/Data-Yaml.html)? If so, you should mention it in the question. (By the way, you can [edit] it to add clarifications.) – duplode Mar 02 '17 at 16:51
  • @duplode yes i am using the yaml package. I have added this to the question. – QuickQuestionGuy Mar 02 '17 at 17:02
  • @QuickQuestionGuy Welcome to [so], we have edit history. There is no need to make your post less readable for future users by including "Edit" in the post itself when you change something. – Anthon Mar 02 '17 at 17:22
  • Tangential note: consider not using `id` for the first field of `Item`, but rather `id_` or something like that. I see how changing that can be somewhat annoying, but `id` is a very common Prelude function, and calling your field that is likely to make readers of your code do a double take. (Note I'm only talking about the Haskell data type definition -- the YAML can of course remain unchanged.) – duplode Mar 02 '17 at 17:40
  • @duplode ah that makes sense, i will keep that in mind. Thank you for your help! – QuickQuestionGuy Mar 02 '17 at 18:09

1 Answers1

2

The easiest way is to create a newtype that implements FromJSON.

import Data.Time
import Data.Yaml

data Item = Item 
  { id :: Int
  , kind :: String
  , createdAt :: UTCTime
  } deriving (Show)

instance FromJSON Item where
  parseJSON (Object v) = 
    Item <$> v .: "id" <*> v .: "kind" <*> v .: "created_at"

newtype Time = Time String deriving (Show, Eq)

instance FromJSON Time where
  parseJSON (Object v) = Time <$> v .: "utc"

Hope that helps.

Erik
  • 957
  • 5
  • 13
  • 2
    It is worth noting that [`UTCTime` has a `FromJSON` instance](https://hackage.haskell.org/package/aeson-1.1.0.0/docs/Data-Aeson.html#t:FromJSON), and so replacing `String` with `UTCTime` in the definition of `Time` will just work, with no further changes being required. – duplode Mar 02 '17 at 17:34