Haskell简明教程(五):处理JSON

这一系列是我学习 Learn You a Haskell For Great Good 之后,总结,编写的学习笔记。

这个系列主要分为五个部分:

本文有参考 《Real World Haskell》

Haskell如何表示JSON

json 是一种数据表现形式,来源于 javascript。JSON中有四种基本的值的类型:

  • number
  • boolean: true/false
  • string
  • null

此外有两种容器类型:

  • array
  • object

那么Haskell我们需要如何表示成JSON呢?我们需要一个 value constructor 加上Haskell自带的类型来表示:

data JValue = JNumber Integer
          | JString String
          | JBoolean Bool
          | JNull
          | JArray [JValue]
          | JObject [(String, JValue)]
            deriving (Show, Ord, Eq)

那么我们要如何输出JSON呢?其类型肯定是这样的:

echoJValue :: JValue -> String

我们分别为 string, bool, null, number 定义好如何输出成JSON:

echoJValue (JNumber i) = show i
echoJValue (JString s) = show s
echoJValue (JBoolean True) = "true"
echoJValue (JBoolean False) = "false"
echoJValue JNull = "null"

array该怎么处理?输出 “[” 之后输出array中的内容,然后输出 “]“,所以是:

echoJValue (JArray a) = "[" ++ handleArray a ++ "]"
    where handleArray [] = ""
          handleArray a = intercalate ", " (map echoJValue a)

而object则是:

echoJValue (JObject a) = "{" ++ handleObject a ++ "}"
    where handleObject [] = ""
          handleObject a = intercalate ", " (map handleKV a)
          handleKV (k,v) = k ++ ": " ++ echoJValue v

此处我们需要导入 Data.List 中的 intercalate 函数:

Prelude> :m Data.List
Prelude Data.List> :t intercalate 
intercalate :: [a] -> [[a]] -> [a]
Prelude Data.List> intercalate ", " ["hello", "world"]
"hello, world"

于是我们便可以输出自定义的JSON了。

Prelude> :l JSON
[1 of 1] Compiling Main             ( JSON.hs, interpreted )
Ok, 1 module loaded.
*Main> JNull
JNull
*Main> JNumber 1
JNumber 1
*Main> JString "hello"
JString "hello"
*Main> JBoolean True
JBoolean True
*Main> JBoolean False
JBoolean False
*Main> JArray [JBoolean True, JBoolean False]
JArray [JBoolean True,JBoolean False]
*Main> echoJValue $ JArray [JBoolean True, JBoolean False]
"[true, false]"
*Main> echoJValue $ JObject [("hello", JNumber 1), ("world", JString "world")]
"{hello: 1, world: \"world\"}"

我们可以把整个文件做成一个Module:

module JSON (
    JValue(..),
    echoJValue
    ) where 

import Data.List (intercalate)


data JValue = JNumber Integer
          | JString String
          | JBoolean Bool
          | JNull
          | JArray [JValue]
          | JObject [(String, JValue)]
            deriving (Show, Ord, Eq)


echoJValue :: JValue -> String
echoJValue (JNumber i) = show i
echoJValue (JString s) = show s
echoJValue (JBoolean True) = "true"
echoJValue (JBoolean False) = "false"
echoJValue JNull = "null"
echoJValue (JArray a) = "[" ++ handleArray a ++ "]"
    where handleArray [] = ""
          handleArray a = intercalate ", " (map echoJValue a)
echoJValue (JObject a) = "{" ++ handleObject a ++ "}"
    where handleObject [] = ""
          handleObject a = intercalate ", " (map handleKV a)
          handleKV (k,v) = k ++ ": " ++ echoJValue v

那我们该要怎样 pretty print呢?这就留作思考吧 :) (提示:Haskell的世界,递归总是那么重要)

总结

到此Haskell教程便结束了,我们从最开始的命令式语言如何抽象,到介绍Haskell的基本语法和要素, TypeClass, Functor, Applicative, Monoid, Monad,到随后我们封装一个简单的JSON模块。简略的 浏览了一下Haskell的面貌,但是Haskell抽象程度很高,为此付出的代价便是不那么容易一眼就看出来, 有时候甚至需要来回看,来回琢磨才能领会其中的意义。

之后我将会写一些散篇,主要是Haskell和现实世界的应用,或者是Haskell的源代码分析。

参考资料:


更多文章
  • 消息分帧(字符串设计或协议设计)的两种形式
  • C, Go, Python的错误处理和异常机制杂谈
  • 好的命名是最好的文档
  • 读《系统之美:决策者的系统思考》
  • Linux高分屏支持
  • GCC默认的头文件搜索路径
  • 读《远见-如何规划职业生涯3大阶段》
  • 后端工程师学前端(五): SASS
  • 后端工程师学前端(四): CSS进阶(盒子模型)
  • 读《投资中最简单的事》
  • 后端工程师学前端(三): CSS进阶(特指度、单位和字体族)
  • 后端工程师学前端(二): CSS基础知识(规则与选择器)
  • Swift语法笔记
  • 读《管理的实践》
  • 后端工程师学前端(一): HTML