简体   繁体   English

防止 Aeson parseJSON 中的未知字段名称

[英]Prevent unknown field names in Aeson parseJSON

With the following type and instance deriving:使用以下类型和实例派生:

{-# LANGUAGE RecordWildCards #-}

import           Data.Aeson
import           Data.Text

data MyParams = MyParams {
    mpFoo :: Maybe Text,
    mpBar :: Maybe Text
} deriving Show

instance FromJSON MyParams where
    parseJSON = withObject "MyParams" $ \q -> do
        mpFoo <- q .:? "foo"
        mpBar <- q .:? "bar"
        pure MyParams {..}

How can I make sure that the following JSON would fail?如何确保以下 JSON 会失败?

{
  "foo": "this is a valid field name",
  "baa": "this is an invalid field name"
}

With the code above, this JSON succeeds because 1. bar is optional, so parseJSON doesn't complain if it doesn't find it, and 2. baa will not throw any error but will instead be ignored.使用上面的代码,这个 JSON 成功了,因为 1. bar是可选的,所以 parseJSON 如果找不到它不会抱怨,2. baa不会抛出任何错误,而是会被忽略。 The combination of (1) and (2) means that typos in field names can't be caught and will be silently accepted, despite generating an incorrect result ( MyParams { foo = Just(this is a valid field name), bar = Nothing } ). (1) 和 (2) 的组合意味着无法捕获字段名称中的拼写错误并将被静默接受,尽管生成了不正确的结果 ( MyParams { foo = Just(this is a valid field name), bar = Nothing } )。

As a matter of fact, this JSON string should also fail:事实上,这个 JSON 字符串也应该失败:

{
  "foo": "this is fine",
  "bar": "this is fine",
  "xyz": "should trigger failure but doesn't with the above code"
}

TL;DR: how can I make parseJSON fail when the JSON contains any field name that doesn't match either foo or bar ? TL;DR:当 JSON 包含与foobar不匹配的任何字段名称时,如何使parseJSON失败?

Don't forget that the q you have access to in withObject is just a HashMap .不要忘记您在withObject中可以访问的q只是HashMap So, you can write:所以,你可以写:

import qualified Data.HashMap.Strict as HM
import qualified Data.HashSet as HS
import Control.Monad (guard)

instance FromJSON MyParams where
    parseJSON = withObject "MyParams" $ \q -> do
        mpFoo <- q .:? "foo"
        mpBar <- q .:? "bar"
        guard $ HM.keysSet q `HS.isSubsetOf` HS.fromList ["foo","bar"]
        pure MyParams {..}

This will guard that the json only has at most the elements "foo" and "bar" .这将保护 json最多只有元素"foo""bar"

But, this does feel like overkill considering aeson gives you all of this for free.但是,考虑到aeson免费为您提供所有这些,这确实感觉有点过头了。 If you can derive Generic , then you can just call genericParseJSON , as in:如果您可以派生Generic ,那么您只需调用genericParseJSON ,如下所示:

{-# LANGUAGE DeriveGeneric #-}

data MyParams = MyParams {
    mpFoo :: Maybe Text,
    mpBar :: Maybe Text
} deriving (Show, Generic)

instance FromJSON MyParams where
  parseJSON = genericParseJSON $ defaultOptions
    { rejectUnknownFields = True
    , fieldLabelModifier = map toLower . drop 2
    }

Here we adjust the default parse options in two ways: first, we tell it to reject unknown fields, which is exactly what you're asking for, and second, we tell it how to get "foo" from the field name "mpFoo" (and likewise for bar ).这里我们通过两种方式调整默认解析选项:首先,我们告诉它拒绝未知字段,这正是您所要求的,其次,我们告诉它如何从字段名称"mpFoo"中获取"foo" ” (同样适用于bar )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM