在Haskell中将JSON字符串解析为记录

5t7ly7z5  于 9个月前  发布在  其他
关注(0)|答案(4)|浏览(76)

我很难理解这一点(我对Haskell还有点陌生),但我发现Text.JSON包的文档有点令人困惑。基本上我有这样的数据记录类型:-

data Tweet = Tweet
    {
        from_user :: String,
        to_user_id :: String,
        profile_image_url :: String,
        created_at :: String,
        id_str :: String,
        source :: String,
        to_user_id_str :: String,
        from_user_id_str :: String,
        from_user_id :: String,
        text :: String,
        metadata :: String
    }

我有一些JSON格式的tweet符合这种类型的结构。我正在努力解决的问题是如何将上面的Map到从下面的代码返回的内容

decode tweet :: Result JSValue

上面的数据类型。我知道我应该创建一个instance JSON Tweet的示例,但我不知道从哪里开始。
任何提示将不胜感激,谢谢!

osh3o9ms

osh3o9ms1#

我建议您使用新的aeson包而不是json包,因为前者性能更好。下面是如何使用aeson将JSON对象转换为Haskell记录:

{-# LANGUAGE OverloadedStrings #-}
module Example where

import Control.Applicative
import Control.Monad
import Data.Aeson

data Tweet = Tweet {
    from_user :: String,
    to_user_id :: String,
    profile_image_url :: String,
    created_at :: String,
    id_str :: String,
    source :: String,
    to_user_id_str :: String,
    from_user_id_str :: String,
    from_user_id :: String,
    text :: String,
    metadata :: String
    }

instance FromJSON Tweet where
    parseJSON (Object v) =
        Tweet <$> v .: "from_user"
              <*> v .: "to_user_id"
              <*> v .: "profile_image_url"
              <*> v .: "created_at"
              <*> v .: "id_str"
              <*> v .: "source"
              <*> v .: "to_user_id_str"
              <*> v .: "from_user_id_str"
              <*> v .: "from_user_id"
              <*> v .: "text"
              <*> v .: "metadata"
    -- A non-Object value is of the wrong type, so use mzero to fail.
    parseJSON _          = mzero

然后使用Data.Aeson.json得到一个attoparsec解析器,它将ByteString转换为Value。在Value上调用fromJSON,试图将其解析到记录中。请注意,这两个步骤中涉及两个不同的解析器,一个Data.Attoparsec.Parser解析器用于将ByteString转换为通用JSON Value,然后一个Data.Aeson.Types.Parser解析器用于将JSON值转换为记录。请注意,这两个步骤都可能失败:

  • 如果ByteString不是有效的JSON值,第一个解析器可能会失败。
  • 如果(有效的)JSON值不包含您在fromJSON实现中提到的字段之一,则第二个解析器可能会失败。

aeson包更喜欢新的Unicode类型Text(在text包中定义),而不是更老的String类型。Text类型比String具有更高的内存效率,并且通常性能更好。我建议您将Tweet类型更改为使用Text而不是String
如果您需要在StringText之间进行转换,请使用Data.Text中定义的packunpack函数。注意,这样的转换需要O(n)时间,所以尽可能避免它们(即始终使用Text)。

vtwuwzda

vtwuwzda2#

您需要为您的类型编写一个showJSONreadJSON方法,该方法使用JSON格式构建Haskell值。JSON包将负责将原始字符串解析为JSValue
你的tweet很可能是一个JSObject,包含一个字符串Map。

  • 使用show查看JSObject,查看字段的布局。
  • 您可以在JSObject上使用get_field查找每个字段。
  • 您可以使用fromJSStringJSString获取常规Haskell字符串。

一般来说,你需要的是,

{-# LANGUAGE RecordWildCards #-}

import Text.JSON
import Text.JSON.Types

instance JSON Tweet where

    readJSON (JSObject o) = return $ Tweet { .. }
            where from_user         = grab o "from_user"
                  to_user_id        = grab o "to_user_id"
                  profile_image_url = grab o "proile_image_url"
                  created_at        = grab o "created_at"
                  id_str            = grab o "id_str"
                  source            = grab o "source"
                  to_user_id_str    = grab o "to_user_id_str"
                  from_user_id_str  = grab o "from_user_id_str"
                  from_user_id      = grab o "from_user_id"
                  text              = grab o "text"
                  metadata          = grab o "metadata"

grab o s = case get_field o s of
                Nothing            -> error "Invalid field " ++ show s
                Just (JSString s') -> fromJSString s'

注意,我使用的是相当酷的通配符语言扩展。
如果没有JSON编码的示例,我就没有什么可提供的建议了。

  • 相关 *

您可以通过示例找到JSON编码的示例示例

  • 在源代码中,对于简单类型。或者在其他依赖于json的包中。
  • AUR消息的一个示例在这里,作为一个(低级)示例。
kjthegm6

kjthegm63#

导入Data.JSon.Generic和Data.Data,然后将deriving(Data)添加到您的记录类型,然后尝试在tweet上使用decodeJSON。

wlsrxk51

wlsrxk514#

我支持@tibbe的回答。但是,我想添加如何在提供的JSON中缺少参数时提供默认值。
在tibbe的回答中,你可以这样做:

Tweet <$> v .: "from_user"
      <*> v .:? "to_user_id"       .!= "some user here"
      <*> v .: "profile_image_url" .!= "url to image"
      <*> v .: "created_at"
      <*> v .: "id_str"             .!= 232131
      <*> v .: "source"

这将在解析JSON时使用默认参数。

相关问题