如何在Haskell中解析具有可选和变体类型的字段的json? [英] how to parse json with field of optional and variant type in Haskell?

查看:95
本文介绍了如何在Haskell中解析具有可选和变体类型的字段的json?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我如何解析此文件中的输入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

更具挑战性的功能部分:

  1. 使用代码段生成的json是简化的版本.例如,它过滤掉在原始js文件中属性可以是功能的情况,例如onSetStatuseffect,以及genesissupernova情况,其中self可以包含功能onHit.为简单起见,该问题不是必需的,但是非常有趣的是,看看像Haskell这样的函数式编程语言如何能够像Javascript这样支持它.
  1. 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 in effect, and cases like genesissupernova where self can contain function onHit. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆