2015-07-15 26 views
5

Aeson ile uğraşırken biraz zaman geçirdim, ancak Cebirsel Veri Türlerini güzel bir şekilde serileştiremiyorum. Ben denedim neAeson kullanarak Haskell ADT'lerini düzenli JSON olarak nasıl derleriz?

geçerli:

data Attach = Attach { tel :: String } 
       deriving (Show) 
$(deriveJSON defaultOptions ''Attach) 

data Fix = Fix { lat :: Double, lng :: Double } 
       deriving (Show) 
$(deriveJSON defaultOptions ''Fix) 

data MsgIn = AttachMsg Attach 
      | FixMsg Fix 
      deriving (Show) 
$(deriveJSON defaultOptions ''MsgIn) 

data MsgIn2 = MsgIn2 { attach :: Maybe Attach, fix :: Maybe Fix } 
      deriving (Show) 
$(deriveJSON defaultOptions ''MsgIn2) 

someFunc :: IO() 
someFunc = do 
    let attach = Attach "+447890" 
    let reply = AttachMsg attach 
    BL.putStrLn (encode reply) 
    let reply2 = MsgIn2 (Just attach) Nothing 
    BL.putStrLn (encode reply2) 

çıktısı:

{"tag":"AttachMsg","contents":{"tel":"+447890"}} 
{"attach":{"tel":"+447890"},"fix":null} 

aradığım çıktısı:

{"attach":{"tel":"+447890"}} 

ama MsgIn türünden MsgIn2 yerine.

( MsgIn2 çıkışı oldukça yakın alır, ama açık bir null var.)

Aeson içinde bunu yapmanın bir yolu var mı?


Güncelleme:

ekledim: {"attach":{"tel":"+447890"}}:

instance ToJSON MsgIn3 where 
    toJSON (AttachMsg3 (Attach tel)) = object ["attach" .= object ["tel" .= tel]] 
... 
let reply3 = AttachMsg3 attach 
BL.putStrLn (encode reply3) 

ve istediğim cevabı aldım.

@bheklilr, yeniden tanımlamak yerine Attach'in (önceden tanımlanmış) serileştirme yöntemini kullanmanın bir yolu var mı?

bazı saçma sözdizimi denedim ama anlaşılır derlenemeyecektir.Ancak değildir:

instance ToJSON MsgIn3 where 
    toJSON (AttachMsg3 (Attach tel)) = object ["attach" .= (toJSON :: Attach)] 
+2

"ToJSON" ve "FromJSON" örneklerini tam olarak istediğiniz şeyi elde etmek için yazabilirsiniz: 'toJSON (AttachMsg (Attach tel)) = object [" attach ". = Object [" tel ". = Tel] ] 've benzer şekilde FixMsg' için. ParseJSON uygulaması çok daha zor olmaz. – bheklilr

+0

@bheklilr Teşekkürler, bu gerçekten yararlı oldu. Bunu bir cevap haline getirebilir misin?Güncellenmiş sorumu görebiliyor musunuz? – fadedbee

cevap

5

Kullanım özel seçenekler. sumEncoding = ObjectWithSingleField kullanarak doğru yapıyı alabilirsiniz, bu da ilk örneğinizi {"AttachMsg":{"tel":"+447890"}}'a indirir. Daha sonra yapıcı etiketlerini constructorTagModifier = myConstructorTag kullanarak ve adları sizin beğeninize göre özelleştiren bir işlev yazarak (ör. AttachMsg -> ek) özelleştirebilirsiniz. Örnek olarak

, size ayrı bir modül içine bu yazma aktarmadan ve defaultOptions yerine myOptions kullanarak istediğiniz çıktıyı alırsınız:

myConstructorTag :: String -> String 
myConstructorTag "AttachMsg" = "attach" 
myConstructorTag x = x 

myOptions :: Options 
myOptions = defaultOptions {sumEncoding = ObjectWithSingleField, constructorTagModifier = myConstructorTag} 

ayrı modül çünkü Şablon burada ihtiyaç duyulan Haskell. Muhtemelen myConstructorTag ve myOptions'ları TH'nin ihtiyaçlarını karşılamak için daha iyi bir şekilde tanımlamanın bir yolu var, ama bunu nasıl yapacağım konusunda hiçbir fikrim yok.

+0

Teşekkürler, bu doğru cevap gibi görünüyor - geç oluyor, bu yüzden yarın deneyeceğim. – fadedbee

+0

Mükemmel çalışır - seçenekleri tanımlamak için ayrı dosya sorun değil. – fadedbee

4

otomatik boş alanları atlamak için aeson alabilirsiniz. Genellikle DeriveGeneric uzantısı ile birlikte bunu:

{-# LANGUAGE OverloadedStrings, DeriveGeneriC#-} 

import Data.Aeson 
import Data.Aeson.Types 
import qualified Data.ByteString.Lazy.Char8 as BL 
import GHC.Generics 

data Attach = Attach { tel :: String } deriving (Show, Generic) 

data Fix = Fix { lat :: Double, lng :: Double } deriving (Show, Generic) 

data Msg = Msg { attach :: Attach, fix :: Maybe Fix } deriving (Show, Generic) 

instance ToJSON Attach 
instance ToJSON Fix 
instance ToJSON Msg where 
    toJSON = genericToJSON (defaultOptions { omitNothingFields = True }) 

main = do 
    let attach = Attach "+447890" 
     reply = Msg attach Nothing 
    BL.putStrLn (encode reply) 

verir ki: Bunun yerine defaultOptions ait

*Main> main 
{"attach":{"tel":"+447890"}} 
+0

Cevabınız için teşekkürler, ama kendimi yeterince açıklamamıştım. Orijinal 'MsgIn' veri türünden' {"attach": {"tel": "+ 447890"}} 'çıkışını istiyorum. – fadedbee

+1

Bu hala soruya cevap vermese bile yararlı bir bilgidir, bu yüzden onu zaten kaldırıyorum. – Carl

+0

Evet, aynı zamanda yararlı olduğu için de reddettim. – fadedbee