映射特定字段时解码记录

时间:2018-09-15 17:21:06

标签: haskell

我有一条看起来像这样的记录:

data List a = List
    { list_count     :: Int
    , list__embedded :: [a]
    -- ^The actual data you’re looking for.
    }
    deriving (Show)

list__embedded字段来自外部API,其中嵌入实际上是一个对象:

{
  "count": 5,
  "_embedded": {
    "payments": [
        // payment records that derive FromJSON
    ]
  }
}

如您所见,_embedded字段实际上是一个对象,但是我只对它的值感兴趣。

我试图通过编写FromJSON实现如下来解决该问题:

instance Aeson.FromJSON a => Aeson.FromJSON (List a) where
    parseJSON (Aeson.Object v) = List
        <$> Aeson.parseField v "count"
        <*> fmap HashMap.elems (Aeson.parseField v "_embedded")
    parseJSON invalid = Aeson.typeMismatch "Not a correct embed for a list" invalid

但是我一直遇到类型不匹配的情况:

• Could not deduce (Aeson.FromJSONKey k0)
    arising from a use of ‘Aeson.parseField’
  from the context: Aeson.FromJSON a
    bound by the instance declaration
    at src/Mollie/API/Types.hs:366:10-52
  The type variable ‘k0’ is ambiguous
  These potential instances exist:
    instance Aeson.FromJSONKey Integer
      -- Defined in ‘aeson-1.2.4.0:Data.Aeson.Types.FromJSON’
    instance Aeson.FromJSONKey Text.Text
      -- Defined in ‘aeson-1.2.4.0:Data.Aeson.Types.FromJSON’
    instance Aeson.FromJSONKey Time.Day
      -- Defined in ‘aeson-1.2.4.0:Data.Aeson.Types.FromJSON’
    ...plus 14 others
    ...plus 14 instances involving out-of-scope types
    (use -fprint-potential-instances to see them all)
• In the second argument of ‘fmap’, namely
    ‘(Aeson.parseField v "_embedded")’
  In the second argument of ‘(<*>)’, namely
    ‘fmap HashMap.elems (Aeson.parseField v "_embedded")’
  In the first argument of ‘(<*>)’, namely
    ‘List <$> Aeson.parseField v "count"
       <*> fmap HashMap.elems (Aeson.parseField v "_embedded")’
    |
369 |         <*> fmap HashMap.elems (Aeson.parseField v "_embedded")
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

我不太确定如何进行这项工作。

1 个答案:

答案 0 :(得分:0)

问题在于HashMap的密钥类型不明确。即使您不使用键,Haskell仍需要知道类型,以便它可以确定要为Aeson.parseField v "_embedded"调用实例化的解析器。您可以通过专门针对elems的类型签名来帮助Haskell:

instance Aeson.FromJSON a => Aeson.FromJSON (List a) where
    parseJSON (Aeson.Object v) = List
        <$> Aeson.parseField v "count"
        <*> fmap elems (Aeson.parseField v "_embedded")
      where elems :: HashMap.HashMap Text.Text a -> [a]
            elems = HashMap.elems
    parseJSON invalid = Aeson.typeMismatch "Not a correct embed for a list" invalid

在此之后,我可以做:

> Aeson.decode "{\"count\": 5, \"_embedded\": {\"payments\": [1,2,3,4]}}" 
          :: Maybe (List [Double])
Just (List {list_count = 5, list__embedded = [[1.0,2.0,3.0,4.0]]})

我不确定您是否打算将付款清单设为list__embedded ,但这应该可以使您走上正轨。