如何使用Aeson解析枚举?

时间:2018-07-02 23:00:44

标签: haskell aeson

我有一个表示枚举的类型:

data FooBar = Foo | Bar deriving (Show, Enum)

我想使用aeson从JSON中的数字值对此进行解析,即JSON文件中的0应该返回Foo,1应该返回Bar。

我的最佳尝试是:

instance FromJSON FooBar where
    parseJSON (Number n) = return (case maybeInt of
                                     Just i -> (toEnum i)
                                     Nothing -> ??)
      where maybeInt = (Scientific.toBoundedInteger n)
    parseJSON _ = mzero

但这并不能真正处理错误情况。最好的方法是什么?

2 个答案:

答案 0 :(得分:1)

怎么样:

@Override
public void onDrawFrame(GL10 gl) {
    if (isVideo) {
        GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureId);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
                GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
                GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
                GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
                GLES20.GL_REPEAT);
        Log.d(TAG, GLES20.glGetError());
    } else {
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
                GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
                GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
                GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
                GLES20.GL_REPEAT);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mImage, 0);
    }
}

@Override  //only called for video
synchronized public void onFrameAvailable(SurfaceTexture surface) {
    mSurfaceTexture.updateTexImage();
    mSurfaceTexture.getTransformMatrix(stMatrix);;
}

哪个会产生如下结果:

import Control.Monad
import Control.Monad.Fail as F

data FooBar = Foo | Bar deriving (Show, Enum, Bounded) -- note Bounded instance

instance FromJSON FooBar where
  -- parseJSON is the one for Int, which internally uses the Scientific function
  -- you used yourself
  -- Control.Monad.Fail.MonadFail Parser, so safeToEnum may have type
  -- safeToEnum :: Int -> Parser FooBar
  -- (Control.Monad.<=<) is (.) but in the Kleisli category
  -- (<=<) :: (b -> m c) -> (a -> m b) -> (a -> m c)
  -- (.)   :: (b ->   c) -> (a ->   b) -> (a ->   c)
  -- (f <=< g) x = do { x' <- g x;   f x' }
  -- (f  .  g) x = let  x' =  g x in f x'
  -- it's always nice when something can just be a composition of smaller things
  parseJSON = safeToEnum <=< parseJSON

-- using fail over mzero gives nicer error messages
-- (mzero = fail "mzero" for Parser, which is *very* informative)
-- this is a reusable function that can also produce Maybes, Eithers, etc.
-- depending on context, and works for lots of enumerations, since
-- many are Bounded (e.g. Bool, Char, etc.), and user-defined ones almost
-- always are.
safeToEnum :: (Enum a, Bounded a, MonadFail m) => Int -> m a
safeToEnum i = if i < min
                  then F.fail $ show i ++ " is less than the minimum, " ++ show min
                  else if i > max
                          then F.fail $ show i ++ " is greater than the maximum, " ++ show max
                          else return result
  where min = fromEnum (minBound `asTypeOf` result)
        max = fromEnum (maxBound `asTypeOf` result)
        result = toEnum i -- hooray laziness
        -- defining result and using it in min and max is safe, because
        -- asTypeOf ignores the second argument; it's just there to clarify
        -- to the type system which instance of Bounded we're talking about
        -- it's not strictly necessary in GHC Haskell, because ScopedTypeVariables
        -- would allow min = fromEnum (maxBound :: a)
        -- but using asTypeOf keeps the language extension count down

答案 1 :(得分:0)

您可以使用您的解析器是forEach(或更确切地说是MonadFail)这一事实,因此可以使用Monad方法使其失败。有了它,您的代码将如下所示:

fail