让 ToJSON 使用 Show Instance

Making ToJSON use Show Instance

如果我的数据类型如下所示:

data SumType = ABC | DEF deriving (Generic, ToJSON)
data MyType = MyType {field1 :: String, field2 :: SumType} deriving (Generic, ToJSON) 

以上将生成一个 JSON,如下所示:{"field1": "blah", "field2":"ABC"}

实际上,MyType 是一种相当复杂的类型,我想保持 ToJSON 派生,但只想调整一个字段以使用 show 实例。

 instance Show SumType where
   show ABC = "abc-blah"
   show DEF = "def-xyz" 

不幸的是,上面的 Show 实例没有被 ToJSON 拾取(我不知道它是否应该被拾取)。为 SumType 手动滚动 ToJSON 似乎 不起作用,因为它需要一个键值对(也许还有另一种方法吗?)。换句话说,JSON 将类似于:{"field1": "blah", "field2":{"field3": "ABC"}}——我只想更改值的字符串化方式,而不是在那里创建新对象。

关于如何在不为 MyType 手动创建 ToJSON 的情况下更改 SumType 的输出字符串的任何建议?所以输出是 {"field1": "blah", "field2":"abc-blah"}

谢谢!

我不明白为 SumType 定义 ToJSON 实例有什么问题。你可以这样做:

import Data.Aeson(ToJSON(toJSON), Value(String))
import Data.Text(pack)

instance ToJSON SumType where
    toJSON = String . pack . show

或者如果您想为 ToJSON 使用 Show 之外的其他字符串:

{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson(ToJSON(toJSON), Value(String))

instance ToJSON SumType where
    toJSON ABC = String "ABC for JSON"
    toJSON DEF = String "DEF for JSON"

现在 Haskell 将 JSON 将 SumType 编码为 JSON 字符串:

Prelude Data.Aeson> encode ABC
"\"ABC for JSON\""
Prelude Data.Aeson> encode DEF
"\"DEF for JSON\""

您可以对 FromJSON 执行相同的操作,将 JSON 字符串解析回 SumType 对象:

{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson(FromJSON(parseJSON), withText)

instance FromJSON SumType where
    parseJSON = withText "SumType" f
        where f "ABC for JSON" = return ABC
              f "DEF for JSON" = return DEF
              f _ = fail "Can not understand what you say!"

如果我们再解析回 JSON 字符串,我们会得到:

Prelude Data.Aeson> decode "\"ABC for JSON\"" :: Maybe SumType
Just ABC
Prelude Data.Aeson> decode "\"DEF for JSON\"" :: Maybe SumType
Just DEF
Prelude Data.Aeson> decode "\"other JSON string\"" :: Maybe SumType
Nothing
Prelude Data.Aeson> decode "{}" :: Maybe SumType
Nothing

因此,如果我们不解码遵循我们定义的模式之一的 JSON 字符串,解析将失败。如果我们不提供 JSON 字符串,而是提供一个空的 JSON 对象,也会发生同样的情况。

Additional notes:

  1. Since SumType here has two values, you can also use a JSON boolean to encode the values.
  2. you can also encode on different JSON objects. You can for instance use the JSON string for ABC, and an integer for DEF. Although I would advice not to do this until there are good reasons (if for instance ABC carries only a string, and DEF ony an integer).
  3. usually the more complex you make encoding, the more complex decoding will be.