Haskell, Aeson: Parsing nested JSON with part unnecessary values

柔情痞子 提交于 2020-01-04 06:32:29

问题


I'm a beginner trying to learn more about Haskell and Aeson by parsing some json files I find online.

I have a .json that looks like this

"Abilities": {
    "Prime": {
          "Ammo": 210,
          "Available": true,
          "Diposition": 3,
          "Type": "Secondary",
          "Class": "Pistols",
          "NormalAttack": {
            "Chance": 0.25,
            "Streak": 2,
            "Rate": 2.67,
            "ShotType": "Hit-Scan",
            "Damage": {
              "Front": 15,
              "Back": 15,
              "Up": 120,
              "Down": 40
            },
              "Status": 0.25
            }
          "Accuracy": 9.8,
          "Reload": 3,
          "Name": "Prime",
          "Magazine": 16,
            },
    "Dual": {
          "Name": "Dual",
          "Available": true,
          "Diposition": 4,
          "Class": "Swords",
          "Type": "Melee",
          "NormalAttack": {
            "Chance": 0.25,
            "Streak": 3,
            "Rate": 1.08,
            "Damage": {
              "Down": 35
            },
              "Status": 0.15
      }
    }
}

The file contains some information that I need, but mostly the ones I don't.

This is what I've tried so far

{-# LANGUAGE DeriveGeneric #-}

module Lib
    ( someFunc
    ) where

import           Data.Aeson
import           Data.HashMap.Strict as HS
import           Data.Maybe
import           Data.Text
import           Data.Map.Strict
import           GHC.Generics


location = "src/this.json"

someFunc :: IO ()
someFunc = putStrLn "someFunc"



data Abilities = Abilities { abilities :: Map String Ability } deriving (Generic, Show)

instance FromJSON Abilities where
  parseJSON = withObject "Abilities" $
    \v -> Abilities
    <$> v .: pack "Abilities"


data Ability = Ability { ammo         :: Double
                       , normalAttack :: Effect
                       , accuracy     :: Double
                       , name         :: String
                       , diposition   :: Double
                       , reload       :: Double
                       } deriving (Generic, Show)

instance FromJSON Ability where
  parseJSON = withObject "Ability" $
    \v -> Ability
    <$> v .: pack "Ammo"
    <*> v .: pack "NormalAttack"
    <*> v .: pack "Accuracy"
    <*> v .: pack "Name"
    <*> v .: pack "Diposition"
    <*> v .: pack "Reload"


data Effect = Effect { chance :: Double
                     , streak :: Double
                     , rate   :: Double
                     , damage :: Damage
                     , status :: Double
                     } deriving (Generic, Show)

instance FromJSON Effect where
  parseJSON = withObject "Effect" $
    \v -> Effect
    <$> v .: pack "Chance"
    <*> v .: pack "Streak"
    <*> v .: pack "Rate"
    <*> v .: pack "Damage"
    <*> v .: pack "Status"


data Damage = Damage { front :: Double
                     , back  :: Double
                     , up    :: Double
                     , down  :: Double
                     } deriving (Generic, Show)

instance FromJSON Damage where
  parseJSON = withObject "Damage" $
    \v -> Damage
    <$> v .: pack "Front"
    <*> v .: pack "Back"
    <*> v .: pack "Up"
    <*> v .: pack "Down"


main = do
  x <- eitherDecodeFileStrict location :: IO (Either String Abilities)
  print x

I know of several different ways of parsing the info

  1. By parsing the file has Aeson Value or Aeson Object and applying Data.HashMap.Strict.toList to convert it in [(k, v)]. The trouble is that on a big file, this method gets really messy.

  2. By using several unConstructor functions which work like this

unConstruct (Constructor x) = case (Constructor x) of Constructor x -> x

But same problem: it gets really unreadable and messy really fast.


That's why I want to parse the .json file as shown in the code above.


But the parsing is failing somewhere in the code because I'm getting Nothing as an output.

The .json file seems to be fine since if I use

main = do
  x <- decodeFileStrict location :: IO (Maybe Object)
  print x

it works just fine, printing file's contents with appropriate default aeson data types.


What could I be doing wrong?


Edit: Implemented fix suggested by @danidiaz in the comments.

Error Msg: Left "Error in $.Abilities['someAbility']: key \"Ammo\" not present"

来源:https://stackoverflow.com/questions/53466541/haskell-aeson-parsing-nested-json-with-part-unnecessary-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!