Data.Aeson和Data.Yaml分别是用于处理JSON和YAML的库,它们具有几乎相同的接口。我为某些类型编写了“基于Aeson”的FromJSON
和ToJSON
个实例:
import Data.Aeson
data PropertyValue = Nested Microformat | Flat Text
deriving (Generic, Show)
instance ToJSON PropertyValue where
toEncoding = genericToEncoding defaultOptions { sumEncoding = UntaggedValue }
instance FromJSON PropertyValue where
parseJSON = genericParseJSON defaultOptions { sumEncoding = UntaggedValue }
虽然Aeson使用这些实例,但Yaml似乎忽略了它们。 (具体来说,我相信Yaml会自动生成编码器和解码器,这要归功于我的类型派生Generic
。)我试图通过将代码更改为
import Data.Aeson
import qualified Data.Yaml as Y
data PropertyValue = Nested Microformat | Flat Text
deriving (Generic, Show)
instance ToJSON PropertyValue where
toEncoding = genericToEncoding defaultOptions { sumEncoding = UntaggedValue }
instance FromJSON PropertyValue where
parseJSON = genericParseJSON defaultOptions { sumEncoding = UntaggedValue }
instance Y.ToJSON PropertyValue where
toEncoding = genericToEncoding defaultOptions { sumEncoding = UntaggedValue }
instance Y.FromJSON PropertyValue where
parseJSON = genericParseJSON defaultOptions { sumEncoding = UntaggedValue }
但GHC抱怨道:
Duplicate instance declarations:
instance ToJSON PropertyValue
-- Defined at src/Microformats.hs:54:10
instance ToJSON PropertyValue
-- Defined at src/Microformats.hs:60:10
是否无法为碰巧具有相同名称的两个不同类定义实例?或者Yaml的ToJSON
实际上和Aeson的相同的类是什么?
更重要的是,是否可以重用我的ToJSON
和FromJSON
实例以避免必须两次编写相同的代码?如果不这样做,是否至少可以在不混淆编译器的情况下编写库ToJSON
和FromJSON
的实例?
答案 0 :(得分:4)
简短回答:ToJSON
和FromJSON
的Data.Aeson
(和Data.Yaml
)类型相同 。 Data.Yaml
实际上只执行ToJSON
Data.Aeson
类型类别的 重新导入 。
如果我们查看Data.Yaml
source code:
(...)#if (defined (ghcjs_HOST_OS)) module Data.Yaml {-# WARNING "GHCJS is not supported yet (will break at runtime once called)." #-} #else module Data.Yaml #endif ( -- * Types Value (..) , Parser , Object , Array , ParseException(..) , prettyPrintParseException , YamlException (..) , YamlMark (..) -- * Constructors and accessors , object , array , (.=) , (.:) , (.:?) , (.!=) -- ** With helpers (since 0.8.23) , withObject , withText , withArray , withScientific , withBool -- * Parsing , parseMonad , parseEither , parseMaybe -- * Classes , ToJSON (..) , FromJSON (..) -- * Encoding/decoding , encode , encodeFile , decode , decodeFile -- ** Better error information , decodeEither , decodeEither' , decodeFileEither -- ** More control over decoding , decodeHelper ) where #if !MIN_VERSION_base(4,8,0) import Control.Applicative(()) #endif import Control.Exception import Data.Aeson ( Value (..), ToJSON (..), FromJSON (..), object , (.=) , (.:) , (.:?) , (.!=) , Object, Array , withObject, withText, withArray, withScientific, withBool )
因此模块会导出ToJSON
类型类,但这只是ToJSON
导入Data.Aeson
的结果,因此类型类实际上是相同的。
因此,程序员方便重新导入(例如,您不必导入Data.Aeson
仅用于实现FromJOSN
),但事实上您仍然使用相同类型,相同类型所有ToJSON
和Y.ToJSON
引用相同的类型类。
由于两者实际上是相同的,因此您无法为同一类型实例化两次相同的类型类,但我们不需要这样做:如果我们为Data.Aeson
(或Data.Yaml
实现它),这就足够了,因为例如用Data.Yaml
(或Data.Aeson
)编写的类型类约束将成功。结果是我们不能(至少没有一些技巧)对ToJSON
实施Data.Yaml
而不是Data.Aeson
。