所以,我通过使用Haskell Aeson库解析以下JSON来解决障碍。
所以说我有以下内容:
"packetX_Name": [
"container",
[
{
"field1": "value1",
"field2": "value2"
},
{
"field1": "value3",
"field2": "value4"
},
{
"field1": "value5",
"field2": "value6"
}
]
],
"packetY_Name": [
"container",
[
{
"field1": "value7",
"field2": "value8"
},
{
"field1": "value9",
"field2": "value10"
}
]
],
etc...
我最好使用这样的数据类型来解析它:
data ExtractedPacket = ExtractedPacket
{ packetName :: String
, packetFields :: [ExtractedPacketField]
} deriving (Show,Eq)
instance FromJSON ExtractedPacket where
parseJSON = blah
data ExtractedPacketField = ExtractedPacketField
{ field1 :: String
, field2 :: String
} deriving (Show,Eq)
instance FromJSON ExtractedPacketField where
parseJSON = blah
得到类似以下的内容:
ExtractedPacket
"packetX_Name"
[ ExtractedPacketField "value1" "value2"
, ExtractedPacketField "value3" "value4"
, ExtractedPacketField "value5" "value6"
]
ExtractedPacket
"packetY_Name"
[ ExtractedPacketField "value7" "value8"
, ExtractedPacketField "value10" "value10"
]
这个JSON示例描述了网络数据包,并且每个数据包都有不同的名称(例如" packetX_Name"),这些名称不能以相同的方式解析" field1"或" field2"可。它每次都会有所不同。当涉及到这种情况时,大多数Aeson例子都没有用。我注意到名为withArray
的API文档中的一个函数与String
匹配,但我对于(Array -> Parser a)
的使用内容感到遗憾
我真正坚持的部分是解析以String"容器"开头的异构数组。然后有一个包含所有对象的数组。到目前为止,我一直在直接索引对象数组,但是类型系统开始成为一个真正的迷宫,我发现很难以一种不丑陋和黑客的方式来解决这个问题。除此之外,Aeson不会产生非常有用的错误消息。
有关如何处理此事的任何想法?
答案 0 :(得分:1)
在更复杂的示例中,最好记住Aeson Value
类型下面是简单的数据结构 - 数组Vector
和对象HashMap
。比我们习惯处理的列表和地图更具异国情调,但仍然是具有Foldable
和Traversable
个实例的数据结构。考虑到这一点,我们可以声明这些实例:
{-# LANGUAGE OverloadedStrings #-}
import qualified Control.Lens as Lens
import qualified Data.Foldable as Foldable
import qualified Data.Text.Strict.Lens as Lens
import Data.Aeson
import Data.Aeson.Types
newtype ExtractedPackets =
ExtractedPackets [ExtractedPacket] deriving (Show)
instance FromJSON ExtractedPackets where
parseJSON (Object o) = do
let subparsers =
[ ExtractedPacket (Lens.view Lens.unpacked key) <$> parseJSON packets
| (key, Array values) <- Lens.itoList o
, packets@(Array _) <- Foldable.toList values]
packets <- sequence subparsers
return (ExtractedPackets packets)
parseJSON invalid =
typeMismatch "ExtractedPackets" invalid
instance FromJSON ExtractedPacketField where
parseJSON (Object o) =
ExtractedPacketField <$> o .: "field1" <*> o .: "field2"
parseJSON invalid =
typeMismatch "ExtractedPacketField" invalid
我们必须对包列表进行newtype,因为FromJSON
已经存在FromJSON a => FromJSON [a]
个实例,并且它没有按照我们想要的方式执行(具体来说,它仅用于处理同类列表) 。
一旦我们这样做,我们就可以得到对象内部的hashmap,并将其键和值作为元组遍历。通过元组映射,我们生成[Parser ExpectedPacket]
,我们可以sequence
生成Parser [ExpectedPacket]
。我在这里使用lens
来做无聊的事情,比如在打包和解包的字符串之间进行转换,或者将hashmap分解为键值元组。如果您不想提取text
,则可以使用unordered-containers
和lens
包来实现相同的目标。
它似乎适用于提供的示例:
λ> eitherDecode bytes :: Either String ExtractedPackets
Right (ExtractedPackets [ExtractedPacket {packetName = "packetX_Name",
packetFields = [ExtractedPacketField {field1 = "value1", field2 =
"value2"},ExtractedPacketField {field1 = "value3", field2 =
"value4"},ExtractedPacketField {field1 = "value5", field2 =
"value6"}]},ExtractedPacket {packetName = "packetY_Name", packetFields
= [ExtractedPacketField {field1 = "value7", field2 =
"value8"},ExtractedPacketField {field1 = "value9", field2 =
"value10"}]}])
最后,我经常发现使用typeMismatch
和eitherDecode
对调试Aeson实例非常有帮助。