Haskellのデータ型をJSON化しRedisへ入れ込む
JSONまわりのライブラリにどんなものがあるのか調べつつ、Data.Aesonの出来の素晴らしさに感心しながらサンプルコードを書いてみました。
JSON化したデータをhedis(Redisクライアント)でRedisへ入れ込み、さらにみんなのPHPでHaskellのデータ型をPHPのインスタンスとして復元するという、
何とも利用価値がない、コードです。
でこれをですね、実行しますとRedisへJSON化されたデータが格納され、その後格納したデータを再度、haskellのデータ型へ戻しています。
{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
import Data.Aeson
import Control.Applicative ((<$>),(<*>))
import Control.Monad (mzero)
import Control.Monad.IO.Class (liftIO)
import qualified Data.ByteString.Internal as I
import Data.ByteString.Lazy.Char8
import Data.Text.Encoding
import Data.Text
import qualified Data.HashMap.Strict as H
import Database.Redis
-- JSON化する痛風型
data TwoFoose =
Nyouichi {
code :: Text,
name :: Text,
popularity :: Integer
} |
Nyouji {
code :: Text,
name :: Text,
popularity :: Integer
} |
Unknown deriving(Show, Eq)
instance FromJSON TwoFoose where
parseJSON (Object val) = case H.lookup "type" val of
Just obj -> case obj of
"Nyouichi" -> Nyouichi <$> val .: "code" <*> val .: "name" <*> val .: "popularity"
"Nyouji" -> Nyouji <$> val .: "code" <*> val .: "name" <*> val .: "popularity"
_ -> return Unknown
parseJSON _ = mzero
instance ToJSON TwoFoose where
toJSON (Nyouichi c n p) = object [
"type" .= decodeUtf8 "Nyouichi",
"code" .= c,
"name" .= n,
"popularity" .= p
]
toJSON (Nyouji c n p) = object [
"type" .= decodeUtf8 "Nyouji",
"code" .= c,
"name" .= n,
"popularity" .= p
]
store :: (ToJSON a, RedisCtx m f) => I.ByteString -> a -> m (f Status)
store key v = set key (toStrict $ encode v)
storeObj :: RedisCtx m f => TwoFoose -> m (f Status)
storeObj v = store (encodeUtf8 (code v)) v
loadObj :: RedisCtx m f => I.ByteString -> m (f (Maybe I.ByteString))
loadObj = get
convert :: Either t (Maybe I.ByteString) -> Maybe TwoFoose
convert (Right (Just bs)) = Data.Aeson.decode (fromStrict bs)
convert _ = Nothing
selfConnectInfo :: ConnectInfo
selfConnectInfo = defaultConnectInfo {
connectHost = "localhost",
connectPort = PortNumber 6379,
connectAuth = Nothing,
connectMaxConnections = 100,
connectMaxIdleTime = 30
}
main :: IO()
main = do
conn <- connect selfConnectInfo
runRedis conn $ do
storeObj ichi
storeObj ji
i <- loadObj "nyou_kouichi"
liftIO $ print $ (convert i)
j <- loadObj "nyou_sanji"
liftIO $ print $ (convert j)
u <- loadObj "majika"
liftIO $ print $ (convert u)
where
ichi = Nyouichi "nyou_kouichi" "尿高一" 888888
ji = Nyouji "nyou_sanji" "尿惨二" 99999999
もちろんRedisデーモンは起動しておいてください
cuomo@karky7 ~ $ runhaskell RedisJson.hs
Just (Nyouichi {code = "nyou_kouichi", name = "\23615\39640\19968", popularity = 888888})
Just (Nyouji {code = "nyou_sanji", name = "\23615\24808\20108", popularity = 99999999})
Just Unknown
cuomo@karky7 ~ $
Redisへ格納されているJSONの確認
cuomo@karky7 ~ $ redis-cli -h localhost
redis localhost:6379> KEYS *
1) "nyou_kouichi"
2) "majika"
3) "nyou_sanji"
redis localhost:6379>
redis 127.0.0.1:6379> get nyou_kouichi
"{\"popularity\":888888,\"name\":\"\xe5\xb0\xbf\xe9\xab\x98\xe4\xb8\x80\",\"code\":\"nyou_kouichi\",\"type\":\"Nyouichi\"}"
cuomo@karky7 ~ $
尿高一さんが入ってますね
JSONをPHPのインスタンスを作成してみる
Redisへ保存されているJSONから、PHPのオブジェクトインスタンスを作成してみる。おれおれオブジェクト指向なのでPHPでは、
ご法度的な技法でも突っ込まないでください 笑...
Redisからすべてのキーを取得して、TwoFooseFactoryでオブジェクトへ変換可能ならインスタンスを作成します。PHPからRedisへアクセスするためPHPのRedisクライアントを入れてください、何でもいいですが、私はこれを入れました。
karky7 ~ # emerge dev-php/pecl-redis
<?php
/**
* 尿酸ズを生成する工場
*/
class TwoFooseFactory {
protected $classTable;
function __construct() {
$this->classTable = array(
'Nyouichi' => 1,
'Nyouji' => 1
);
}
public function createInstance($json) {
$i = json_decode($json);
if($i) {
$instance = NULL;
$class_name = $this->getClassName($i->type);
if($class_name != '') {
$instance = new $class_name($i->code, $i->name, $i->popularity);
} else {
$instance = new Unknown();
}
return $instance;
}
throw new Exception("Json decode error.");
}
protected function getClassName($cname) {
if(array_key_exists($cname, $this->classTable)) {
return $cname;
}
return '';
}
}
/**
* PHP5.4.0以降で利用可能
*/
trait SharedFunc {
protected $code;
protected $name;
protected $popularity;
function __construct($code='', $name='', $popularity='') {
$this->code = $code;
$this->name = $name;
$this->popularity = $popularity;
}
public function __get($name) {
return $this->{$name};
}
}
/**
* クラス定義
*/
class Unknown {
use SharedFunc;
}
class TwoFooseBase {
use SharedFunc;
}
class Nyouichi extends TwoFooseBase {
}
class Nyouji extends TwoFooseBase {
}
/**
* main
*/
function main() {
$redis = new Redis();
$redis->pconnect('127.0.0.1', 6379);
$keys = $redis->keys('*');
$factory = new TwoFooseFactory();
foreach($keys as $i => $k) {
$i++;
$json = $redis->get($k);
$Instance = $factory->createInstance($json);
print("= {$i} =\n");
print("Instance: " . get_class($Instance) . "\n");
print(" Code: " . $Instance->code . "\n");
print(" Name: " . $Instance->name . "\n");
print(" Pop: " . $Instance->popularity . "\n");
print("\n");
}
}
main();
?>
PHPを実行して、JSONへアクセスしてみる
cuomo@karky7 ~ $ php RedisJson.php
= 1 =
Instance: Nyouichi
Code: nyou_kouichi
Name: 尿高一
Pop: 888888
= 2 =
Instance: Unknown
Code:
Name:
Pop:
= 3 =
Instance: Nyouji
Code: nyou_sanji
Name: 尿惨二
Pop: 99999999
cuomo@karky7 ~ $
ほーぅら、JSONを利用した、Haskellデータ型とPHPのオブジェクトインスタンスの相互利用が可能になりました。
何とか、もう少しでいいのでHaskell力が欲しい今日この頃です
それからお酒の飲みすぎの
痛風にはご注意ください。