本文介绍了FromJSON从多个字段中列出一个列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 我有一个解析对象,看起来有点像这样: {data: $ b $ virtio0:一些文本,virtio1:blah,ide2:一些其他文本,cores:1,mem:512, ... }, { //相同... } ] } 现在我基本上想把它解析成[VM],但我的问题是那些编号字段。根据虚拟机配置,它可能会或可能不会有virtioX字段,ideX字段......并且我没有看到提前知道的方法,也没有猜测数字。 我认为最好的可能是定义一个类似Virtio |的磁盘类型萨塔| IDE等等,以及该值的文本字段,然后让每个虚拟机都有一个类型为[Disk]的磁盘。像这样: data DiskType = Virtio |萨塔| IDE data Disk = Disk {diskType :: DiskType,diskPath :: Text} data VM = VM {cores :: Int,disks :: [Disk],mem :: Int,...} 这会很好,但是如何解析直接在VM json中的那些随机字段对象到列表中?解决方案虽然我不认为自己是Haskell专家,甚至更少是Aeson专家,认为我发现了一些可行的方法。采取它的是什么。 以下代码都使用这个模块声明和这些导入: 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) 我稍微更改了类型声明: data DiskType = Virtio |萨塔| IDE导出(显示)数据磁盘= 磁盘{diskType :: DiskType,diskNumber :: Int,diskPath :: Text} 派生(显示)数据VM = VM { cores :: Int,disks :: [Disk],mem :: Int}派生(显示) 最明显的区别是我为 Disk 类型添加了 diskNumber ,这样它就可以捕获磁盘类型,以及与磁盘属性相关的文本。 另一个变化是我将所有类型都作为 Show 。这只能够测试我的代码是否工作。 首先,我定义了一个辅助函数,它可以在给定前缀后找到数字: findNumber :: Read a =>文字 - >文字 - >也许一个 findNumber前缀candidate = stripPrefix候选前缀>> =(readMaybe。unpack) 示例: * Main Data.Text> findNumber(packide)(packide2)::可能是Int 只是2 *主Data.Text> findNumber(packsata)(packsata0)::可能是Int 只是0 * Main Data.Text> findNumber(packfoo)(packbar):: Maybe Int Nothing 这使我可以编写一个函数来查找 Object 中的所有磁盘: findDisks :: HashMap Text Value - > [Disk] findDisks = foldlWithKey'文件夹[] 其中 findVirtio k s =翻转(Disk Virtio)s< $> findNumbervirtiok findSata k s = flip(Disk Sata)s< $> findNumbersatak findIde k s = flip(Disk IDE)s< $> findNumberidek 文件夹acc k(String s)= acc ++ toList(findVirtio ks< |> findSata ks< |> findIde ks)文件夹acc _ _ = acc Object 是一个类型别名对于 HashMap Text Value ,所以这个函数接受一个 Object 作为输入,并返回 Disk 它可以找到的值。 这足以定义一个 FromJSON $ VM : instance FromJSON VM其中 parseJSON = withObjectVM$ \o - > do let disks = findDisks o cores mem return $ VM cores disks mem 为了测试这个效果,我创建了这个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 执行时,它会打印解码值: 只是[VM {cores = 1,disks = [磁盘{diskType = IDE,diskNumber = 2,diskPath =其他文本},磁盘{diskType = Virtio,diskNumber = 1,diskPath =blah},Disk {diskType = Virtio,diskNumber = 0,diskPath =一些文本}],mem = 512}] 注意,这里解析的JSON只是一个数组的VM对象。我没有在 data 属性中包含外部容器对象,但如果您需要帮助,我认为这应该是一个单独的问题:) I have an object to parse that looks a bit like this :{ "data": [ { "virtio0": "some text", "virtio1": "blah", "ide2": "some other text", "cores": 1, "mem": 512, ... }, { // The same ... } ]}Now I basically want to parse that into a [VM], but my problem is those numbered fields. Depending on the VM config, it might or might not have virtioX fields, ideX fields .. and I don't see a way to know in advance, nor to guess the numbers.I was thinking the best might be to define a Disk type that would contain something like Virtio | Sata | IDE and so on for the type, and a Text field for the value, then have each VM have a [Disk] in it's type. Something like this :data DiskType = Virtio | Sata | IDEdata Disk = Disk {diskType :: DiskType, diskPath :: Text}data VM = VM {cores :: Int, disks :: [Disk], mem :: Int, ...}That would be great, but how do I parse those random fields that I have directly inside the VM json object into a list ? 解决方案 While I don't consider myself a Haskell expert, and even less of an Aeson expert, I think I've found something that works. Take it for what it is.The following code all makes use of this module declaration and these imports:{-# LANGUAGE OverloadedStrings #-}module Main whereimport Control.Applicative ((<$>), (<|>))import Data.Aesonimport Data.ByteString.Lazy (ByteString)import Data.HashMap.Lazy (HashMap, foldlWithKey')import Data.Foldable (toList)import Data.Text (Text, stripPrefix, unpack)import Text.Read (readMaybe)I changed the type declarations slightly:data DiskType = Virtio | Sata | IDE deriving (Show)data Disk = Disk { diskType :: DiskType, diskNumber :: Int, diskPath :: Text } deriving (Show)data VM = VM { cores :: Int, disks :: [Disk], mem :: Int } deriving (Show)The most notable difference is that I added diskNumber to the Disk type, so that it can capture both the number after the disk type, as well as the text associated with the disk property.The other change was that I made all types be instances of Show. This was only to be able to test whether or not my code works.First, I defined a little helper function that can find the number after a given prefix:findNumber :: Read a => Text -> Text -> Maybe afindNumber prefix candidate = stripPrefix prefix candidate >>= (readMaybe . unpack)Examples:*Main Data.Text> findNumber (pack "ide") (pack "ide2") :: Maybe IntJust 2*Main Data.Text> findNumber (pack "sata") (pack "sata0") :: Maybe IntJust 0*Main Data.Text> findNumber (pack "foo") (pack "bar") :: Maybe IntNothingThis enabled me to write a function that finds all the disks in an Object: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 _ _ = accObject is a type alias for HashMap Text Value, so this function takes an Object as input, and returns a list of the Disk values that it could find.This is enough to define an instance of FromJSON for VM:instance FromJSON VM where parseJSON = withObject "VM" $ \o -> do let disks = findDisks o cores <- o .: "cores" mem <- o .: "mem" return $ VM cores disks memIn order to test that this works, I created this JSON string:myJson :: ByteStringmyJson = "[\ \{\ \\"virtio0\": \"some text\",\ \\"virtio1\": \"blah\",\ \\"ide2\": \"some other text\",\ \\"cores\": 1,\ \\"mem\": 512\ \}\ \]"and used it from main:main :: IO ()main = do let vms = decode myJson :: Maybe [VM] print vmsWhen executed, it prints the decoded value: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}]Notice that the JSON parsed here is simply an array of VM objects. I didn't include the outer container object with the data property, but if you need help with that, I think that ought to be a separate question :) 这篇关于FromJSON从多个字段中列出一个列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 10-28 21:50