Aeson解码JSON对象,可以是字符串也可以是int

时间:2016-07-05 21:20:15

标签: haskell aeson

我正在处理来自REST服务器的一些复杂格式的JSON响应。为了解码它们,我有几种数据类型来处理不同的嵌套对象。例如:

... Other types ...

data Profile =
  Profile { fields :: [KVPair]
  } deriving (Show)

instance FromJSON Profile where
  parseJSON (Object v) =
    Profile <$> v .: "Fields" 
  parseJSON _ = mzero

data KVPair =
  KVPair { key :: Int
         , value :: String
  } deriving (Show)

instance FromJSON KVPair where
  parseJSON (Object v) =
    KVPair <$> v .: "Key"
           <*> v .: "Value" 
  parseJSON _ = mzero

除最终的KVPair类型外,一切正常。我的JSON对象都有整数键;但是,值可以是整数或字符串:

      {
        "Key": 0,
        "Value": "String Value!"
      },
      {
        "Key": 1,
        "Value": 42
      }

现在我想我可以为我的值解码添加另一个和类型,它由StringInt组成,但我宁愿避免为此添加一个全新的类型。 Aeson有一种简单的方法来处理这种情况吗?

2 个答案:

答案 0 :(得分:5)

有两个简单的修复方法。一个是简单地写

data KVPair = KVPair { key :: Int, value :: Value }

并保留所有其他代码相同。消费者需要检查Value以查看它是字符串还是数字。

可能更好的方法是简单地提供两个可转换为所需格式的替代解析器。例如,保持KVPair定义不变,可以写

showInt :: Int -> String
showInt = show

instance FromJSON KVPair where
    parseJSON (Object v)
        =   KVPair
        <$> v .: "Key"
        <*> (v .: "Value" <|> (showInt <$> v .: "Value"))

两个方面最好的方法是保留关于周围的StringInt以拒绝其他类型值的信息; e.g。

data KVPair = KVPair { key :: Int, value :: Either String Int }

instance FromJSON KVPair where
    parseJSON (Object v)
        =   KVPair
        <$> v .: "Key"
        <*> (   (Left  <$> v .: "Value")
            <|> (Right <$> v .: "Value")
            )

答案 1 :(得分:1)

您只需使用Aeson Value类型来处理具有可以是任何JSON值的字段的对象。