如何在Haskell的Aeson中获取HashMap (Object)的值?

3

我正在通过解决一些在线练习问题来熟悉一些Haskell库。

我有一些代码,输出如下:

Object (fromList [("ABC", String "123")])

它也可能是

Object (fromList [("123", String "ABC")])
Object (fromList [(String "123", String "ABC")])
Object (fromList [("123", "ABC")])

我需要提取的是"123"

使用类型为(.:) :: FromJSON a => Object -> Text -> Parser a.:函数来提取给定键的值会导致此错误

 Couldn't match type ‘Value’ with ‘HashMap Text Value’                                                         
  Expected type: Object
    Actual type: Value

我的最佳猜测是我需要编写一个解析器,但我不知道如何着手做或者该寻找什么。

产生错误的代码:

x <- (eitherDecode <$> simpleHttp url) :: IO (Either String DataSet)
  case x of
    Left er   -> print er
    Right an -> do
      let l = S.toList (data1 an)
      print $ l .: "ABC"

DataSet的定义如下:

newtype DataSet = DataSet {
                   data1  :: Object
                   } deriving (Show, Generic)

如果我要替换

print $ (Data.List.head l) .: "ABC"

只需要

print $ (Data.List.head l)

我理解为

Object (fromList [("ABC", String "123")])

你没有包含实际产生错误的代码 - 你能否请包含进来?例如,可能是你犯了一个我们在这里看不到的简单拼写错误。 - AJF
@AJFarmar添加了产生错误的代码。 - atis
非常好,谢谢。我不确定我是否找到了问题,但我可以问一下为什么您编写 let l = L.toList (data1 an) 而不是 let l = data1 an?这似乎可能是问题所在。 - AJF
这是因为 data1 an 输出的是一个 Object 类型。an 包含许多 Object (fromList [("ABC", "123")])。对其应用 toList 可以将其转换为列表并逐个提取出每个元素。 - atis
可能是这样,但是l仍然是一个列表,而你正在编写l .: "ABC",这不是良好类型化的,那么你为什么要写那个呢? - AJF
显示剩余2条评论
3个回答

2

ObjectValue 类型 的几个构造函数之一。

Haskell Constructor | JSON Syntax
Object              | {"key": "value"}
String              | "hello"
Number              | 123
Array               | [1, 2, 3]

注意,在这种情况下,构造函数Object不是类型Object的构造函数。[见注]。
错误来自于将一个Value传递给某个期望Object的函数。如果遇到其他情况,您需要定义程序应该执行的操作。
或者,由于您有data1 an :: Object,您可以在其中查找所需的键。我不确定S.toList的类型是什么,但您似乎正在将您的Object转换为Value,然后将其传递给需要一个Object.:
最后请注意: Object(fromList [("ABC", String "123")])是一个具有一个键值对的单个对象ValuefromList是一种从其部分(而不是通过解析JSON字符串)创建对象的方法。

我在Haskell方面仍然是个初学者,所以这可能是一个奇怪的问题,但是查找键不需要类型为Object而不是上一个print语句的结果Value吗?将data1应用于an会给我一堆对象。对其应用toList让我选择我想要的元素。从该元素内部提取“123”toList data1 an等同于[Value,Value,Value,....]。应用head意味着我最终只得到Value - atis
如果您发布整个程序,将会更有帮助。.:需要一个对象。根据您提供的data1 :: DataSet -> Object的定义,data1 an :: Object。您看到了什么表明data1 an是“一堆对象”?您从哪里导入了toList?有几个同名的函数。 - bergey

0

Value 类型中快速获得信息的一种简单方法是

Value 编码为 ByteString

编码的类型为 encode :: ToJSON a => a -> ByteString

所以在你的代码中:

...
  case x of
    Left er   -> print er
    Right an -> do
      let l = S.toList (data1 an)
          x = (encode . snd) l  -- you can replace snd with fst if you want "ABC" instead of "123"
          y = decode x :: Maybe (HashMap String String)
          case y of
              Nothing -> print "got nothing"
              Just a -> print $ Data.HashMap.Strict.toList a

这将输出类似于这样的列表:

[("123")]

现在您可以通过简单的函数提取值。

希望这有所帮助。

要了解更多关于如何更好地解析 JSON 文件的信息,我建议您认真阅读https://artyom.me/aeson


0
这里有几种方法可以从Value数据类型中拆开Object构造器。
您可以创建一个函数来解开:
unwrapValue :: Value -> Object
unwrapValue (Object x) = x
unwrapValue _ = error "No Object available"

注意:该函数会返回一个错误,因为有可能 Value 不是一个 Object

此外,不要因为 Object 同时是 Value 的构造函数和 aeson 中的类型而感到困惑!

你也可以内联解包,但这也不安全,可能会导致运行时错误。例如:

getNum :: Array -> Either String Scientific
getNum someArray = flip parseEither someArray $ \arr -> do
  let Just (Object obj) = arr !? 1 -- Unsafe unwrap Object constructor from Value (also unwraps Just constructor from Maybe)
  (Number num) <- obj .: "myNumber" -- Unsafe unwrap Number constructor from Value

  return num

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接