json的基本类型为——string, numbers, Booleans以及null,定义json类型如下

-- file: Json.hs
module Json where
data JValue = JString String
           |  JNumber Double
           |  JBool Bool
           |  JNull 
           |  JObject [(String, JValue)]
           |  JArray [JValue]
              deriving(Eq, Ord, Show)

打印Json类型数据

-- file: PutJson.hs
module PutJson where
 
import Data.List (intercalate)
import Json
 
renderJValue :: JValue –> String
renderJValue (JString s) = show s
renderJValue (JNumber n) = show n
renderJValue (JBool True) = “true”
renderJValue (JBool False) = “false”
renderJValue JNull         = “null”
 
renderJValue (JObject o) = "{" ++ pairs o ++ "}"
    where pairs [] = ""
          pairs ps = intercalate "," (map renderPair ps)
          renderPair (k, v) = show k ++ ":" ++ renderJValue v
renderJValue (JArray a) = "[" ++ values a ++ "]"
    where values [] = ""
          values vs = intercalate "," (map renderJValue vs)
 
putJValue :: JValue –> IO()
putJValue v = putStrLn (renderJValue v)

最后编写调用文件

-- file: Main.hs
module Main where
 
main = putJValue (JObject [("foo", JNumber 1), ("bar", JBool False)])

在命令行中输入如下指令

ghc –o demo Json.hs PutJson.hs Main.hs

生成demo.exe,然后在命令行中输入demo,即可得到JValue的打印字符串。如果JValue的值结构非常复杂,则通过这种方法打印出来显得对人类不够友好。那么,如何友好的打印出来呢?

假设我们有一个模块Prettify,这个模块的API将JValue转换为Doc而不是上面的String,我们重写render函数如下,

-- file: PrettyJson.hs
renderJValue :: JValue –> Doc
renderJValue (JBool True)  = text “true”
renderJValue (JBool False) = text “false”
renderJValue JNull         = text “null”
renderJValue (JNumber num) = double num
renderJValue (JString str) = string str

其中,Doc类型,text,double和string函数由我们的Prettify模块实现。

先给出string函数的定义

-- file: PrettyJson.hs
string :: String –> Doc
string = enclose '"' '"' . hcat . map oneChar

这个定义中使用了自由点风格(自由点风格),本来函数是从左向右运算的,使用自由点风格后,等价于

-- file: PrettyJson.hs
string :: String –> Doc
string s = enclose '"' '"' (hcat (map oneChar s))                                          (1)

可以看出,使用这种风格,省去参数s,并将小括号替换为点号。这个定义中,出现了各种函数,现在来依次分解。enclose函数是在一个Doc对象的前面和后面分别附加字符,其定义如下

-- file: PrettyJson.hs
enclose :: Char –> Char –> Doc –> Doc
enclose left right x = char left <> x <> char right

char是一个函数,用于将一个Char字符类型转换为Doc类型,目前我们还不知道其方法体,先用undefined表示其方法体,这样, 签名如下,

-- file: PrettyJson.hs
char :: Char –> Doc
char c = undefined

<>是一个中缀函数,写在两个运算分量中间时,看作一个运算符,其运算优先级比作为函数的char底,故enclose函数方法体可以这么看,char函数将Char字符类型参数left和right转换为两个Doc类型参数,然后与另一个Doc类型参数 x 用两个 <> 运算符连接,容易知道,<>连接两个Doc类型参数,得到另一个Doc类型结果,签名如下,其实,这个<>运算符类似于String类型的(++)运算符,即连接两个Doc参数,生成一个新的Doc类型结果。

-- file: PrettyJson.hs
(<>) :: Doc –> Doc –> Doc
a <> b = undefined

我们再看(1),现在可以知道string函数的作用就是对(hcat (map oneChar s)) 这个Doc类型值两边添加双引号 ("),于是我们接着看hcat函数,这个函数类似于list的连接函数concat (注意跟 <> 的区别),签名如下

-- file: PrettyJson.hs
hcat :: [Doc]-> Doc
hcat xs = undefined

接下来,由于map函数是对列表s的每个元素应用oneChar函数,故我们只要看一下oneChar函数是干什么用的就可以了。由于s是一个String类型,故oneChar函数是将一个Char类型转为一个Doc类型,给出函数的详细定义如下,

-- file: PrettyJson.hs
oneChar :: Char –> Doc
oneChar c = case lookup c simpleEscapes of 
              Just r –> text r
              Nothing | mustEscape –> hexEscape c
                      | otherwise  ->  char c
    where mustEscape c = c < ' ' || c == '\x7f' || c > '\xff'
 
simpleEscapes :: [(Char, String)]
simpleEscapes = zipWith ch "\b\n\f\r\t\\\"/" "bnfrt\\\"/"
    where ch a b = (a, ['\\', b]

这段函数定义篇幅稍微有点大,我们一点一点来看,首先simpleEscapes是一个二元元组的列表,元组的第一项为一个简单转义字符(\b, \n, \f, \r, \t, \\, \", /),第二项是将第一项表现出来,比如 '\b' ,表现为 "\\b" ,可以写一个测试如下

Prelude> :load PrettyJson.hs
Prelude> take 8 simpleEscapes
[('\b',"\\b"),('\n',"\\n"),('\f',"\\f"),('\r',"\\r"),('\t',"\\t"),('\\',"\\\\"),('"',"\\\""),('/',"\\/")]

lookup函数是检查字符c是否在simpleEscapes中某一元组的第一项,如果是,则返回这个转义字符的字符串表现形式,如果字符c不是简单的转义字符,则如果是其他需要转义的字符,对字符c应用hexEscape函数,否则,就是简单的字符,不需要转义,直接应用char函数将Char类型转为Doc类型。

还有一个函数hexEscape还未给出解析。我们直接给出定义如下

-- file: PrettyJson.hs
hexEscape :: Char –> Doc
hexEscape c | d < 0x10000 = smallHex d
            | otherwise   = astral (d – 0x10000)
    where d = ord c

根据字符c的ascii码值是否小于0x10000,对字符c的码值进行不同的处理。

-- file: PrettyJson.hs
smallHex :: Int –> Doc
smallHex x = text "\\u"
          <> text (replicate (4 – length h) ‘0’)
          <> text h
    where h = showHex x ""

其中,showHex函数来自Numeric库,需要在文件的开头import这个库。showHex函数的功能是以16进制打印一个数。replicate函数是将一个指定类型a重复n次,生成一个a类型的列表

replicate :: Int –> a –> [a]

根据hexEscape的定义,字符c的ascii码值(16进制)的长度不超过4位(根据guard表达式为d< 0x10000得到),故4 – length h 的范围为[0, 4],smallHex函数的作用是这样打印参数x,先打印"\\u", 然后x的16进制位数如果不足4位,补0使得满足4位,然后打印x的16进制值。

smallHex函数能打印的值最大不超过0xffff,而有效的unicode字符范围上限一直到0x10ffff,对 (0xffff, 0x10ffff) 的字符,应用 astral 函数,其定义给出如下

-- file: PrettyJson.hs
astral :: Int –> Doc
astral n = smallHex (a + 0xd800) <> smallHex (b + 0xdc00)
    where a = (n `shiftR` 10).&. 0x3ff
          b = n .&. 0x3ff

其中,shiftR是右移函数,(.&.)是按位与。我们看一下astral函数是如何处理参数n的,对于参数n,取其低10位作为b,取其第11位到第20位(次低10位)作为a,当然了,前面说到参数的范围为 (0xffff, 0x10ffff) ,值位数就是17位到20位,故 a 是高10位,b 是低10位。

Haskell Json数据处理的更多相关文章

  1. 【多端应用开发系列1.1.1 —— Android:使用新浪API V2】服务器Json数据处理——Json数据概述

    [前白] 一些基础的东西本系列中就不再详述了,争取尽量写些必不可少的技术要点. 由于本系列把Web Service 构建放到了第二部分,Android项目就采用新浪微博API v2作为服务器端. [原 ...

  2. iOS开发——数据解析Swift篇&简单json数据处理

    简单json数据处理 //loadWeather var url = NSURL(string: "http://www.weather.com.cn/adat/sk/101240701.h ...

  3. Spark SQL JSON数据处理

    背景   这一篇可以说是“Hive JSON数据处理的一点探索”的兄弟篇.   平台为了加速即席查询的分析效率,在我们的Hadoop集群上安装部署了Spark Server,并且与我们的Hive数据仓 ...

  4. python接口自动化(十九)--Json 数据处理---实战(详解)

    简介 上一篇说了关于json数据处理,是为了断言方便,这篇就带各位小伙伴实战一下.首先捋一下思路,然后根据思路一步一步的去实现和实战,不要一开始就盲目的动手和无头苍蝇一样到处乱撞,撞得头破血流后而放弃 ...

  5. JSON数据处理框架Jackson精解第一篇-序列化与反序列化核心用法

    Jackson是Spring Boot默认的JSON数据处理框架,但是其并不依赖于任何的Spring 库.有的小伙伴以为Jackson只能在Spring框架内使用,其实不是的,没有这种限制.它提供了很 ...

  6. ASP.MVC时间类型json数据处理

    服务端返回DateTime属性如果用自带的json方法返回的数据如下: 有2种办法解决一种是采用服务端解决方案,一种是使用前端解决方案 1.前端解决方案 第一步:对Date进行扩展 // 对Date的 ...

  7. Qt json 数据处理

    用到的头文件 #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> json解 ...

  8. json数据处理实战:Kafka+Flume+Morphline+Solr+Hue数据组合索引

    背景:Kafka消息总线的建成,使各个系统的数据得以在kafka节点中汇聚,接下来面临的任务是最大化数据的价值,让数据“慧”说话. 环境准备: Kafka服务器*3. CDH 5.8.3服务器*3,安 ...

  9. Json数据处理

    1.字符串转换为Json数组:取json对象属性值. String st="[{"tradeDate":"2016-09-27","trad ...

随机推荐

  1. Setting up Ubuntu in CoLinux–changing local/keyboard to be English

    Today i installed the CoLinux with Ubuntu package, but the default locale setting of the system is G ...

  2. JS的基本概念

    JS的基本概念 任何语言的核心都必然会描述这门语言最基本的工作原理.而描述的内容通常都要涉及这门语言的语法,操作符,数据类型,内置功能等用于构建复杂解决方案的概念.Ecma-262通过叫做EcmaSc ...

  3. 《Head First Python》学习笔记 01

    Python 3提供了70多个内建函数,这是Python“功能齐全”的观点:Python已经包含足够多的内置功能,使你能完成大多数工作,而不必依赖第三方代码. Python 列表: Python列表是 ...

  4. icon 图标下载

    1. http://www.easyicon.net/ 2.http://www.iconpng.com/

  5. POJ 2409 Let it Bead 组合数学

    题目地址: http://poj.org/problem?id=2409 给你一串珠子有m个,用n种不同的颜色涂色,问有多少种分法. 用polay定理求解,对于排成一排的带编号的小球,按照某一种方案改 ...

  6. install cuda5 on ubuntu12.04

    1. sudo apt-get install libglapi-mesa 2. sudo apt-get install freeglut3-dev build-essential libx11-d ...

  7. 关于EL表达式的生效时间(猜想)

    通过ajax与服务端异步交互的时候,在服务端将某些变量或对象设置到request等域里,此时页面上的EL表达式是获取不到ajax异步交互时设置在request等域里的变量或对像的. 我猜测可能EL表达 ...

  8. Prefix the choice with ! to persist it to bower.json ? Answer (问你选择哪个1,2,3.........)

    Unable to find a suitable version for underscore, please choose one by typing on e of the numbers be ...

  9. 【01背包】HDU 1171 Big Event in HDU

    Problem Description Nowadays, we all know that Computer College is the biggest department in HDU. Bu ...

  10. html5之datalist标签

    当我看到这个标签的时候,其实我是很愤怒的.因为我以前实现过这个标签的功能,当时是无比的费劲.什么js库呀,function呀.我靠,统统去屎吧,哥有datalist了.那种感觉就好像自己千辛万苦去追去 ...