如何在Haskell中解析具有可选和变体类型的字段的json? [英] how to parse json with field of optional and variant type in Haskell?
问题描述
我如何解析此文件中的输入json? https://github.com/smogon/pokemon-showdown/blob/master/data/moves.js
How I can parse the input json inside this file ? https://github.com/smogon/pokemon-showdown/blob/master/data/moves.js
对于次要属性和标志属性?它们是可选的,并且包含变量类型.
For the secondary and flags properties? They are optional and contains variant type.
一个最小的例子就是这个:
A minimal example would be this one:
[
{},
{
"secondary": false
},
{
"secondary": {
"chance": 10,
"boosts": {
"spd": -1
}
}
},
{
"secondary": {
"chance": 30,
"volatileStatus": "flinch"
}
},
{
"secondary": {
"chance": 30
}
},
{
"secondary": {
"chance": 10,
"self": {
"boosts": {
"atk": 1,
"def": 1,
"spa": 1,
"spd": 1,
"spe": 1
}
}
}
},
{
"secondary": {
"chance": 10,
"status": "brn"
}
},
{
"secondary": {
"chance": 50,
"self": {
"boosts": {
"def": 2
}
}
}
},
{
"secondary": {
"chance": 100,
"self": {}
}
},
{
"secondary": {
"chance": 50,
"boosts": {
"accuracy": -1
}
}
}
]
为方便起见,您可以选择将此代码段附加到js文件的末尾并使用node move.js
运行它.两个有效的json文件将保存到您的磁盘中.一个是json对象的列表,另一个是带有字符串作为键的对象.
For your convenience, you can choose to attach this snippet to the end of the js file and run it using node move.js
. Two valid json files will be saved to your disk. One is a list of json objects while the other is an object with string as key.
var fs = require('fs');
fs.writeFile("moves_object.json", JSON.stringify(BattleMovedex), function(err) {}); // 1. save a json object with string key
var jsonList = []
for (var key of Object.keys(BattleMovedex)) {
jsonList.push(BattleMovedex[key]);
}
fs.writeFile("moves.json", JSON.stringify(jsonList), function(err) { // 2. save as a list of json object
if (err) {
console.log(err);
}
});
以锅炉板为起点:
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}
import Data.Aeson
import Data.Text
import Control.Applicative
import Control.Monad
import qualified Data.ByteString.Lazy as B
import Network.HTTP.Conduit (simpleHttp)
import GHC.Generics
-- | Type of each JSON entry in record syntax.
data Person =
Person { firstName :: !Text
, lastName :: !Text
, age :: Int
, likesPizza :: Bool
} deriving (Show,Generic)
-- Instances to convert our type to/from JSON.
instance FromJSON Person
instance ToJSON Person
-- | Location of the local copy, in case you have it,
-- of the JSON file.
jsonFile :: FilePath
jsonFile = "pizza.json"
-- | URL that points to the remote JSON file, in case
-- you have it.
jsonURL :: String
jsonURL = "http://daniel-diaz.github.io/misc/pizza.json"
-- Move the right brace (}) from one comment to another
-- to switch from local to remote.
{--
-- Read the local copy of the JSON file.
getJSON :: IO B.ByteString
getJSON = B.readFile jsonFile
--}
{--}
-- Read the remote copy of the JSON file.
getJSON :: IO B.ByteString
getJSON = simpleHttp jsonURL
--}
main :: IO ()
main = do
-- Get JSON data and decode it
d <- (eitherDecode <$> getJSON) :: IO (Either String [Person])
-- If d is Left, the JSON was malformed.
-- In that case, we report the error.
-- Otherwise, we perform the operation of
-- our choice. In this case, just print it.
case d of
Left err -> putStrLn err
Right ps -> print ps
更具挑战性的功能部分:
- 使用代码段生成的json是简化的版本.例如,它过滤掉在原始js文件中属性可以是功能的情况,例如
onSetStatus
和effect
,以及genesissupernova
情况,其中self
可以包含功能onHit
.为简单起见,该问题不是必需的,但是非常有趣的是,看看像Haskell这样的函数式编程语言如何能够像Javascript这样支持它.
- The generated json using the code snippet is a simplified version. For example, it filter out the cases where, in the original js file, properties can be functions, like
onSetStatus
as ineffect
, and cases likegenesissupernova
whereself
can contain functiononHit
. For simplicity, it is not required in this question but would be very interesting to see how Functional Programming language like Haskell can support this like Javascript.
仅供参考:
如果您熟悉c ++,可能会发现在这篇文章中更容易理解相同的问题:
如何解析具有类型组成的json文件std :: optional和std :: variant
FYI:
If you are familiar with c++, you might find it easier to understand the same problem in this post:
How to parse json file with type composition of std::optional and std::variant
推荐答案
这是对mover.json
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
module Main where
import Control.Applicative
import Data.Maybe
import Data.Text (Text)
import GHC.Generics
import Data.Aeson
main :: IO ()
main = do
result <- eitherDecodeFileStrict "/tmp/helloworld/movers.json"
case ( result :: Either String [Move]) of
Left error -> print error
Right ms -> print (length ms)
data Move = Move
{ num :: Int
, accuracy :: Either Int Bool
, secondary :: Maybe (Either Bool Secondary)
} deriving (Generic, Show)
data Secondary = Secondary
{ chance :: Maybe Int
, volatileStatus :: Maybe Text
, boosts :: Maybe Boosts
, self :: Maybe Self
, status :: Maybe Text
} deriving (Generic, Show)
data Boosts = Boosts
{ atk :: Maybe Int
, def :: Maybe Int
, spa :: Maybe Int
, spd :: Maybe Int
, spe :: Maybe Int
} deriving (Generic, Show)
data Self = Self
{ boosts :: Maybe Boosts
} deriving (Generic, Show)
instance FromJSON Move where
parseJSON (Object v) = Move
<$> v .: "num"
<*> ( (Left <$> v .: "accuracy")
<|> (Right <$> v .: "accuracy")
)
<*> ( fmap (fmap Left) (v .:? "secondary")
<|> fmap (fmap Right) (v .:? "secondary")
)
instance FromJSON Secondary
instance FromJSON Boosts
instance FromJSON Self
这篇关于如何在Haskell中解析具有可选和变体类型的字段的json?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!