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}]