Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
FromJSON从多个字段创建列表_Json_Haskell_Aeson - Fatal编程技术网

FromJSON从多个字段创建列表

FromJSON从多个字段创建列表,json,haskell,aeson,Json,Haskell,Aeson,我有一个要解析的对象,看起来有点像这样: { "data": [ { "virtio0": "some text", "virtio1": "blah", "ide2": "some other text", "cores": 1, "mem": 512, ... }, { // The same ... } ] } data DiskType = Virtio |

我有一个要解析的对象,看起来有点像这样:

{
  "data":
  [
    {
      "virtio0": "some text",
      "virtio1": "blah",
      "ide2": "some other text",
      "cores": 1,
      "mem": 512,
      ...
    },
    {
      // The same ...
    }
  ]
}
data DiskType = Virtio | Sata | IDE
data Disk     = Disk {diskType :: DiskType, diskPath :: Text}
data VM       = VM {cores :: Int, disks :: [Disk], mem :: Int, ...}
现在我基本上想把它解析成一个[VM],但我的问题是那些编号字段。根据VM配置,它可能有也可能没有virtioX字段、ideX字段。。我看不到提前知道的方法,也看不到猜测数字的方法。 我在想,最好是定义一个磁盘类型,该类型包含Virtio | Sata | IDE等内容,并为值定义一个文本字段,然后让每个虚拟机在其类型中都有一个[Disk]。大概是这样的:

{
  "data":
  [
    {
      "virtio0": "some text",
      "virtio1": "blah",
      "ide2": "some other text",
      "cores": 1,
      "mem": 512,
      ...
    },
    {
      // The same ...
    }
  ]
}
data DiskType = Virtio | Sata | IDE
data Disk     = Disk {diskType :: DiskType, diskPath :: Text}
data VM       = VM {cores :: Int, disks :: [Disk], mem :: Int, ...}

那太好了,但是我如何将VM json对象中直接包含的随机字段解析成一个列表呢?

如果你说只有9个virtio和2个ide,那么一个简单且可能不是那么简单的方法就是使用Data.Foldable中的asum函数(这是来自各种解析库的一般选择)

导入控件。应用程序
来自JSON VM的实例,其中
parseJSON=withObject“VM”$\o->do

虽然我不认为自己是哈斯克尔专家,更不用说是一位专家,我想我已经找到了一些行之有效的方法。照原样去做吧

以下代码均使用此模块声明和这些导入:

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Control.Applicative ((<$>), (<|>))
import Data.Aeson
import Data.ByteString.Lazy (ByteString)
import Data.HashMap.Lazy (HashMap, foldlWithKey')
import Data.Foldable (toList)
import Data.Text (Text, stripPrefix, unpack)
import Text.Read (readMaybe)
最显著的区别是,我将
diskNumber
添加到
Disk
类型中,以便它可以捕获磁盘类型后的数字以及与Disk属性相关联的文本

另一个变化是,我将所有类型都设置为
Show
的实例。这只是为了测试我的代码是否有效

首先,我定义了一个小辅助函数,可以在给定前缀后查找数字:

findNumber :: Read a => Text -> Text -> Maybe a
findNumber prefix candidate =
  stripPrefix prefix candidate >>= (readMaybe . unpack)
示例:

*Main Data.Text> findNumber (pack "ide") (pack "ide2") :: Maybe Int
Just 2
*Main Data.Text> findNumber (pack "sata") (pack "sata0") :: Maybe Int
Just 0
*Main Data.Text> findNumber (pack "foo") (pack "bar") :: Maybe Int
Nothing
这使我能够编写一个函数来查找
对象中的所有磁盘

findDisks :: HashMap Text Value -> [Disk]
findDisks = foldlWithKey' folder []
  where
    findVirtio k s = flip (Disk Virtio) s <$> findNumber "virtio" k
    findSata   k s = flip (Disk Sata)   s <$> findNumber "sata"   k
    findIde    k s = flip (Disk IDE)    s <$> findNumber "ide"    k
    folder acc k (String s) =
      acc ++ toList (findVirtio k s <|> findSata k s <|> findIde k s)
    folder acc _ _ = acc
为了测试这是否有效,我创建了以下JSON字符串:

myJson :: ByteString
myJson =
  "[\
    \{\
      \\"virtio0\": \"some text\",\
      \\"virtio1\": \"blah\",\
      \\"ide2\": \"some other text\",\
      \\"cores\": 1,\
      \\"mem\": 512\
    \}\
  \]"
并从
main
使用它:

main :: IO ()
main = do
  let vms = decode myJson :: Maybe [VM]
  print vms
执行时,它打印解码值:

Just [VM {cores = 1, disks = [Disk {diskType = IDE, diskNumber = 2, diskPath = "some other text"},Disk {diskType = Virtio, diskNumber = 1, diskPath = "blah"},Disk {diskType = Virtio, diskNumber = 0, diskPath = "some text"}], mem = 512}]

请注意,这里解析的JSON只是一个VM对象数组。我没有包含带有
数据
属性的外部容器对象,但是如果您需要帮助,我认为这应该是一个单独的问题:)

我只会将键存储为附加值。这是什么意思?我基本上不知道Aeson是如何工作的,我最近发现了如何使用v.:“stuff”来使用不同的名称,可能使用一个条件,但仅此而已。医生对我来说有点毛茸茸的,我不太熟悉埃森的支持。但有一种方法是使用
解码
对象
类型中。并将该对象解析为您的数据类型。“解码混合类型对象”一节可能很有用。您是创建这个json对象还是由某个程序创建的?我的意思是,你能去掉
“virtioX”
“ideX”
中的
X
后缀吗?啊,不,我无法控制原始的json,它是Proxmox API。至于“解码混合类型对象”,谢谢,我来看看!我不知道会有多少,可能是0,可能是10,可能是50。但我想我可以让它在99次迭代或类似的情况下循环,我想一定有一个最大值..好吧,我刚刚测试了这个,它正是我需要的!非常感谢,我不能说我完全理解,所以看起来我有点阅读要做:)
Just [VM {cores = 1, disks = [Disk {diskType = IDE, diskNumber = 2, diskPath = "some other text"},Disk {diskType = Virtio, diskNumber = 1, diskPath = "blah"},Disk {diskType = Virtio, diskNumber = 0, diskPath = "some text"}], mem = 512}]