【问题标题】:Decode record while mapping a specific field在映射特定字段时解码记录
【发布时间】:2018-09-16 03:18:56
【问题描述】:

我的记录是这样的:

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")
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

我不太确定如何完成这项工作。

【问题讨论】:

    标签: haskell


    【解决方案1】:

    问题在于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 的一个元素,但这应该会让您走上正轨。

    【讨论】:

    • 我不敢相信这是“这么简单”:D 谢谢,它立即解决了我的问题。
    猜你喜欢
    • 1970-01-01
    • 2012-06-21
    • 1970-01-01
    • 2022-07-12
    • 1970-01-01
    • 2015-12-30
    • 1970-01-01
    • 2016-11-27
    • 2013-07-15
    相关资源
    最近更新 更多