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 | IDE
data 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 ?
If as you said there are only 9 virtio and 2 ide, one simple and perhaps not so elegent way to do is to use the asum function from Data.Foldable (which is generalised choice from various parsing libraries)
I haven't tried the code yet. For further reference, see this link for a comprehensive guide of haskell JSON parsing with the Aeson library.
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:
I changed the type declarations slightly:
The most notable difference is that I added
diskNumber
to theDisk
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:
Examples:
This enabled me to write a function that finds all the disks in an
Object
:Object
is a type alias forHashMap Text Value
, so this function takes anObject
as input, and returns a list of theDisk
values that it could find.This is enough to define an instance of
FromJSON
forVM
:In order to test that this works, I created this JSON string:
and used it from
main
:When executed, it prints the decoded value:
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 :)