2016-05-26 4 views
1

は私がタグなしでAesonでレコード合計データ型をシリアル化する方法は?

data AB = A 
    { 
    ab :: Text 
    , a :: Text 
    } 
    | B 
    { 
    ab :: Text 
    , b :: Text 
    } deriving (Generic) 

今、私はそれは、次のJSONに生成されたAをシリアル化するためにアイソーンを使用する場合、たとえば、複数のコンストラクタを持つデータ型を持っている:

{ 
    "tag": "A", 
    "ab": "some text", 
    "a": "some text" 
} 

私はそれがSumEncodingを使用することが可能だということを知っていますコンストラクタがどのように処理されるかを操作するが、私が望むものは見つけられない。

どうやらtagフィールドをシリアル化されたJSONで省略することはできますか?私は一方向のシリアライズ(それを非直列化する理由はありません)が必要ですが、データ型はかなり手動でシリアライズする方法を書いています。

答えて

3

ハック方法は、単に結果のオブジェクトからtagを削除することです:それはおそらくときあなたを噛まないように戻ってくる

{-# LANGUAGE DeriveGeneriC#-} 
import Data.Aeson 
import Data.HashMap.Strict 
import Data.String 
import Data.Text 
import GHC.Generics 

data AB 
    = A { ab :: Text 
     , a :: Text 
     } 
    | B { ab :: Text 
     , b :: Text 
     } 
    deriving Generic 

instance ToJSON AB where 
    toJSON ab = case genericToJSON defaultOptions ab of 
    Object o -> Object (delete (fromString "tag") o) 
    _ -> error "impossible" 

一般的なインスタンスは必然的に変更されますが、そうする場合は、あなたが選択している技術的な負債を意識してください。

+0

私はそれを受け取り、Aesonに寄付することをお勧めします。そのハックはまさに​​私が探していたものでした。ありがとう! – klappvisor

2

だけToJSONインスタンスを自分で定義します。

instance ToJSON AB where 
    toJSON (A ab a) = object [ "ab" .= ab, "a" .= a ] 
    toJSON (B ab a) = object [ "ab" .= ab, "a" .= a ] 

ab1 = A "foo" "bar" 
ab2 = A "abc" "def" 

*Main> import qualified Data.ByteString.Lazy.Char8 as LBS 
*Main LBS> LBS.putStrLn $ encode ab2 
{"ab":"abc","a":"def"} 
*Main LBS> LBS.putStrLn $ encode ab1 
{"ab":"foo","a":"bar"} 
+0

比較的大きな数のプロパティ(いくつかは「多分」、いくつかのネストされている)、およびコンストラクタの数のために、手動でシリアル化するコードを書くことは避けたかったのです。しかし、他のオプションがなければ、私はそれに従います。 – klappvisor

+0

@klappvisor各フィールドの型が 'ToJSON'インスタンスである限り、フィールドがネストされているかどうかは問題ではありません。 –

+0

@DanielWagner私は 'defaultOptions {omitNothingFields = True}'を使用しますので、除外したいのであれば – klappvisor

関連する問題