整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

程序员都应该了解的一种数据格式之 JSON

者 | 猪哥

责编 | maozz

JSON的诞生原因是因为XML整合到HTML中各个浏览器实现的细节不尽相同,所以道格拉斯·克罗克福特(Douglas Crockford) 和 奇普·莫宁斯达(Chip Morningstar)一起从JS的数据类型中提取了一个子集,作为新的数据交换格式,因为主流的浏览器使用了通用的JavaScript引擎组件,所以在解析这种新数据格式时就不存在兼容性问题,于是他们将这种数据格式命名为 “JavaScript Object Notation”,缩写为 JSON,由此JSON便诞生了!

今天我们来学习一下JSON的结构形式、数据类型、使用场景以及注意事项吧!

JSON格式

上面我们知道JSON是从JavaScript的数据类型中提取出来的子集,那JSON有几种结构形式呢?又有哪些数据类型呢?他们又分别对应着JavaScript中的哪些数据类型呢?

JSON的2种结构形式,键值对形式和数组形式。

举了一个JSON的实例,就是键值对形式的,如下:

{

"person": {

"name": "pig",

"age": "18",

"sex": "man",

"hometown": {

"province": "江西省",

"city": "抚州市",

"county": "崇仁县"

}

}

}

这种结构的JSON数据规则是:一个无序的“‘名称/值’对”集合。一个对象以 {左括号 开始, }右括号 结束。每个“名称”后跟一个 :冒号 ;“‘名称/值’ 对”之间使用 ,逗号 分隔。

因为大多数的时候大家用的JSON可能都是上面那种key-value形式,所以很多人在讲解JSON的时候总是会忽略数组形式,这一点是需要注意的。

那JSON的数组形式是怎么样的呢?举一个实例吧!

["pig", 18, "man", "江西省抚州市崇仁县"]

数组形式的JSON数据就是值(value)的有序集合。一个数组以 [左中括号 开始, ]右中括号 结束。值之间使用 ,逗号 分隔。

JOSN的6种数据类型

上面两种JSON形式内部都是包含value的,那JSON的value到底有哪些类型,而且上期我们说JSON其实就是从Js数据格式中提取了一个子集,那具体有哪几种数据类型呢?

  1. string:字符串,必须要用双引号引起来。

  2. number:数值,与JavaScript的number一致,整数(不使用小数点或指数计数法)最多为 15 位,小数的最大位数是 17。

  3. object:JavaScript的对象形式,{ key:value }表示方式,可嵌套。

  4. array:数组,JavaScript的Array表示方式[ value ],可嵌套。

  5. true/false:布尔类型,JavaScript的boolean类型。

  6. :空值,JavaScript的。

以上数据形式图片来源JSON官方文档:http://www.json.org/json-zh.html

JSON使用场景

介绍完JSON的数据格式,那我们来看看JSON在企业中使用的比较多的场景。

接口返回数据和序列化。JSON用的最多的地方莫过于Web了,现在的数据接口基本上都是返回的JSON,具体细化的场景有:

  1. Ajxa异步访问数据

  2. RPC远程调用

  3. 前后端分离后端返回的数据

  4. 开放API,如百度、高德等一些开放接口

  5. 企业间合作接口

这种API接口一般都会提供一个接口文档,说明接口的入参、出参等,

一般的接口返回数据都会封装成JSON格式,比如类似下面这种

{

"code": 1,

"msg": "success",

"data": {

"name": "pig",

"age": "18",

"sex": "man",

"hometown": {

"province": "江西省",

"city": "抚州市",

"county": "崇仁县"

}

}

}

程序在运行时所有的变量都是保存在内存当中的,如果出现程序重启或者机器宕机的情况,那这些数据就丢失了。一般情况运行时变量并不是那么重要丢了就丢了,但有些内存中的数据是需要保存起来供下次程序或者其他程序使用。

保存内存中的数据要么保存在数据库,要么保存直接到文件中,而将内存中的数据变成可保存或可传输的数据的过程叫做序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。

正常的序列化是将编程语言中的对象直接转成可保存或可传输的,这样会保存对象的类型信息,而JSON序列化则不会保留对象类型!

为了让大家更直观的感受区别,猪哥用代码做一个测试,大家一目了然

  1. Python对象直接序列化会保存class信息,下次使用loads加载到内存时直接变成Python对象。

  2. JSON对象序列化只保存属性数据,不保留class信息,下次使用loads加载到内存可以直接转成dict对象,当然也可以转为Person对象,但是需要写辅助方法。

对于JSON序列化不能保存class信息的特点,那JSON序列化还有什么用?答案是当然有用,对于不同编程语言序列化读取有用,比如:我用Python爬取数据然后转成对象,现在我需要将它序列化磁盘,然后使用Java语言读取这份数据,这个时候由于跨语言数据类型不同,所以就需要用到JSON序列化。

存在即合理,两种序列化可根据需求自行选择!

最后就是生成Token和配置文件

首先声明Token的形式多种多样,有JSON、字符串、数字等等,只要能满足需求即可,没有规定用哪种形式。

JSON格式的Token最有代表性的莫过于JWT(JSON Web Tokens)。

随着技术的发展,分布式web应用的普及,通过Session管理用户登录状态成本越来越高,因此慢慢发展成为Token的方式做登录身份校验,然后通过Token去取Redis中的缓存的用户信息,随着之后JWT的出现,校验方式更加简单便捷化,无需通过Redis缓存,而是直接根据Token取出保存的用户信息,以及对Token可用性校验,单点登录更为简单。

使用JWT做过app的登录系统,大概的流程就是:

  1. 用户输入用户名密码

  2. app请求登录中心验证用户名密码

  3. 如果验证通过则生成一个Token,其中Token中包含:

  4. 用户的uid、Token过期时间、过期延期时间等,然后返回给app

  5. app获得Token,保存在cookie中,下次请求其他服务则带上

  6. 其他服务获取到Token之后调用登录中心接口验证

  7. 验证通过则响应

JWT登录认证有哪些优势:

  1. 性能好:服务器不需要保存大量的session

  2. 单点登录(登录一个应用,同一个企业的其他应用都可以访问):使用JWT做一个登录中心基本搞定,很容易实现。

  3. 兼容性好:支持移动设备,支持跨程序调用,Cookie 是不允许垮域访问的,而 Token 则不存在这个问题。

  4. 安全性好:因为有签名,所以JWT可以防止被篡改。更多JWT相关知识自行在网上学习,本文不过多介绍!

说实话JSON作为配置文件使用场景并不多,最具代表性的就是npm的package.json包管理配置文件了,下面就是一个npm的package.json配置文件内容。

{

"name": "server", //项目名称

"version": "0.0.0",

"private": true,

"main": "server.js", //项目入口地址,即执行npm后会执行的项目

"scripts": {

"start": "node ./bin/www" ///scripts指定了运行脚本命令的npm命令行缩写

},

"dependencies": {

"cookie-parser": "~1.4.3", //指定项目开发所需的模块

"debug": "~2.6.9",

"express": "~4.16.0",

"http-errors": "~1.6.2",

"jade": "~1.11.0",

"morgan": "~1.9.0"

}

}

但其实JSON并不合适做配置文件,因为它不能写注释、作为配置文件的可读性差等原因。

配置文件的格式有很多种如:toml、yaml、xml、ini等,目前很多地方开始使用yaml作为配置文件格式。

JSON在Python中的使用

最后我们来看看Python中操作JSON的常用方法有哪些,在Python中操作JSON时需要引入json标准库。

import json

类型转换

Python类型转JSON:json.dump

# 1、Python的dict类型转JSON

person_dict = {'name': 'pig', 'age': 18, 'sex': 'man', 'hometown': '江西抚州'}

# indent参数为缩进空格数

person_dict_json = json.dumps(person_dict, indent=4)

print(person_dict_json, '\n')

# 2、Python的列表类型转JSON

person_list = ['pig', 18, 'man', '江西抚州']

person_list_json = json.dumps(person_list)

print(person_list_json, '\n')

# 3、Python的对象类型转JSON

person_obj = Person('pig', 18, 'man', '江西抚州')

# 中间的匿名函数是获得对象所有属性的字典形式

person_obj_json = json.dumps(person_obj, default=lambda obj: obj.__dict__, indent=4)

print(person_obj_json, '\n')

执行结果:

JSON转Python类型:json.loads

# 4、JSON转Python的dict类型

person_json = '{ "name": "pig","age": 18, "sex": "man", "hometown": "江西抚州"}'

person_json_dict = json.loads(person_json)

print(type(person_json_dict), '\n')

# 5、JSON转Python的列表类型

person_json2 = '["pig", 18, "man", "江西抚州"]'

person_json_list = json.loads(person_json2)

print(type(person_json_list), '\n')

# 6、JSON转Python的自定义对象类型

person_json = '{ "name": "pig","age": 18, "sex": "man", "hometown": "江西抚州"}'

# object_hook参数是将dict对象转成自定义对象

person_json_obj = json.loads(person_json, object_hook=lambda d: Person(d['name'], d['age'], d['sex'], d['hometown']))

print(type(person_json_obj), '\n')

执行结果如下:

对应的数据类型

上面我们演示了Python类型与JSON的相互转换,最开始的时候我们讲过JSON有6种数据类型,那这6种数据类型分别对应Python中的哪些数据类型呢?

需要注意的点

JSON的键名和字符串都必须使用双引号引起来,而Python中单引号也可以表示为字符串,所以这是个比较容易犯的错误!

Python类型与JSON相互转换的时候到底是用load/dump还是用loads\dumps?

  • 他们之间有什么区别?

  • 什么时候该加s什么时候不该加s?

这个我们可以通过查看源码找到答案:

不加s的方法入参多了一个fp表示filepath,最后多了一个写入文件的操作。

所以我们在记忆的时候可以这样记忆:

加s表示转成字符串(str),不加s表示转成文件。

Python自定义对象与JSON相互转换的时候需要辅助方法来指明属性与键名的对应关系,如果不指定一个方法则会抛出异常!

相信有些看的仔细的同学会好奇上面使用json.dumps方法将Python类型转JSON的时候,如果出现中文,则会出现:

\u6c5f\u897f\u629a\u5dde

这种东西,这是为什么呢?

原因是:Python 3中的json在做dumps操作时,会将中文转换成unicode编码,并以16进制方式存储,而并不是UTF-8格式!

总结

今天我们学习了JSON的2种形式,切记JSON还有[...]这种形式的。

学习了JSON的6种数据类型他们分别对于Python中的哪些类型。

了解了JSON的一些使用场景以及实际的例子。

还学习了在Python中如何使用JSON以及需要注意的事项。

一个JSON知识点却分两篇长文(近万字)来讲,其重要性不言而喻。因为不管你是做爬虫、还是做数据分析、web、甚至前端、测试、运维,JSON都是你必须要掌握的一个知识点

本文为作者投稿,版权归作者个人所有。

一篇文章我们介绍了一个html/xml解析器——htmlparser,这篇文章我们介绍另外一个解析模块htmlparser2,后者是对前者的重构,同时对前者的API做了部分兼容。

用法简介

安装

const { Parser } = require('htmlparser2');
const parser = new Parser(handler, options);
parser.parseComplete('html/xml内容');

写法

const { Parser } = require('htmlparser2');
const parser = new Parser(handler, options);
parser.parseComplete('html/xml内容');

htmlparser2提供了一个解析器——Parser,初始化它至少需要一个handler,options是可选的。

handler是一个对象,在这个对象上可以设置很多的钩子函数,Parser解析时会在每个阶段运行对应的钩子函数。

以下是可以设置的所有的钩子函数,

  • onopentag(<str> name, <obj> attributes)
  • onopentagname(<str> name)
  • onattribute(<str> name, <str> value)
  • ontext(<str> text)
  • onclosetag(<str> name)
  • onprocessinginstruction(<str> name, <str> data)
  • oncomment(<str> data)
  • oncommentend()
  • oncdatastart()
  • oncdataend()
  • onerror(<err> error)
  • onreset()
  • onend()
  • htmlparser模块是通过正则表达式来解析html内容的,而htmlparser2则不同,它会按顺序读取html的每个字符,并且推测后面字符是标签名、属性还是其他的类型,所以htmlparser2在解析完每一个标签后都会运行相应的钩子函数。

    先来看一下例子,

    图1

    图1中设置了所有的钩子函数以便来说明每个钩子函数的作用,运行一下,

    图2

    对照图1和图2就能看出来每个钩子函数的运行时机,这其中有以下几个钩子函数需要注意一下。

    • oncdatastart和oncdataend会在解析<![CDATA[文本]]>标签时触发,但是触发的前提是选项设置了recognizeCDATA=true或者xmlMode=true;
    • onprocessinginstruction会在解析<!xxyy>或者<?xxyy>这样的标签时触发,xx和yy中间可以有分隔符(斜杠或者空格),分隔符前面的字符串就是钩子函数中的name的值;
    • 运行parseComplete方法会执行reset钩子函数,如果仅仅执行parser的write或者end方法并不会执行reset方法。

    除了自定义handler以外,htmlparser2还提供了几个handler,比如DomHandler,用法如下:

    图3

    运行一下,我们看看结果,

    图4

    如果4所示,DomHandler处理的结果是以数组的形式输出的,在每个单元数据中还可以拿到上一个、下一个以及父节点的数据。

    htmlparser2还可以通过操作流Stream解析内容,写法如下:

    图5

    总结

    这篇文章和上一篇是姊妹篇,都是介绍解析html/xml内容的模块,通过对比,我们发现htmlparser2模块功能更强大一些,也更灵活一些,同时也兼容htmlparser模块的一些接口。虽然两者功能类似,但是这给了我们更多的选择性。

    喜欢我的文章就关注我吧,有问题可以发表评论,我们一起学习,共同成长!

    or...in语句解析

    <script>
    var json={a: 12, b: 5};
    for(var i in json)
    {
    alert(i+'='+json[i]);
    }
    </script>

    eval() 函数可计算某个字符串, 并执行其中的的 JavaScript 代码。

    服务器端脚本代码:

    <?php
    $row=array('username'=>'lisi','password'=>'222222');
    echo json_encode($row);
    /*$data=array(
    array('name'=>'zhangsan','age'=>18),
    array('name'=>'lisi','age'=>30)
    );
    echo json_encode($data);
    */
    ?>

    var json=eval('('+value+')'); 主要是针对关联数组

    返回:"{name:'zhangsan',age:18}"

    访问方式:json.username+json.password


    var json=eval(value); 主要是针对索引数组

    返回:"[{name:'zhangsan',age:18},{name:'lisi',age:20}]"

    访问方式:json[0].name+json[0].age

    注意:索引数组的解析也可以采用 var json=eval(value);

    <script language="javascript" src="public.js"></script>
    <script>
    var xhr=createxhr(); //创建ajax对象, 代码见ajax | ajax封装GET和POST
    xhr.open('post','demo05.php');
    xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
    xhr.onreadystatechange=function(){
    if(xhr.readyState==4 && xhr.status==200){
    var value=xhr.responseText; //返回的是字符串
    //1)
    var json=eval('('+value+')'); //返回是json对象
    alert(json.username+json.password);
    //2)
    //var json=eval(value); //返回是json数组对象
    //alert(json[1].name+json[1].age);
    }
    };
    xhr.send(null);
    </script>

    返回:"{name:’zhangsan’,age:18}"

    解析格式:eval('('+value+')');

    返回:"[{name:'zhangsan',age:18},{name:'lisi',age:20}]"

    解析格式:eval(value);

    也可以采用eval('('+value+')');

    实例1:

    <html>
    <head>
    <title>新建网页</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="description" content="" />
    <meta name="keywords" content="" />
    <script type="text/javascript">
    function f1(){
    //ajax去服务器获得json信息
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
    if(xhr.readyState==4 && xhr.status==200){
    //alert(xhr.responseText);//字符串{"north":"wolf","helan":"pig","germany":"dog"}
    var info = eval('('+xhr.responseText+')');
    //也可写成:eval("var info="+xhr.responseText);
    document.write(info.north);
    document.write(info.helan);
    document.write(info.germany);
    }
    }
    xhr.open('get','03.php');
    xhr.send(null);
    }
    //javascript把一个字符串变为对象
    //var a = '{"north":"wolf","helan":"pig","germany":"dog"}';
    //eval(参数字符串)
    //eval("var obj="+a);//eval('var obj={"north":"wolf","helan":"pig","germany":"dog"}');
    //document.write(obj);//访问对象
    </script>
    </head>
    <body>
    <h2>静态网站,javascript对json的接收处理</h2>
    <input type="button" value="触发" onclick="f1()" />
    </body>
    </html>
    <?php
    //对外提供json信息
    header("Cache-Control:no-cache,must-revalidate");
    $animal = array('north'=>'wolf','helan'=>'pig','germany'=>'dog');
    echo json_encode($animal); //{"north":"wolf","helan":"pig","germany":"dog"}
    ?>

    在javascript解析{"north":"wolf","helan":"pig","germany":"dog"}

    采用:var info = eval('('+xhr.responseText+')'); 语法

    也可写成:eval("var info="+xhr.responseText);

    实例2:

    <html>
    <head>
    <title>新建网页</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="description" content="" />
    <meta name="keywords" content="" />
    <script type="text/javascript">
    function f1(){
    //ajax去服务器获得json信息
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
    if(xhr.readyState==4 && xhr.status==200){
    //alert(xhr.responseText);//数组 ["wolf","pig","dog"]
    var info = eval(xhr.responseText);
    document.write(info[0]+info[1]+info[2]);
    }
    }
    xhr.open('get','03.php');
    xhr.send(null);
    }
    </script>
    </head>
    <body>
    <h2>静态网站,javascript对json的接收处理</h2>
    <input type="button" value="触发" onclick="f1()" />
    </body>
    </html>
    <?php
    //对外提供json信息
    header("Cache-Control:no-cache,must-revalidate");
    $animal = array('wolf','pig','dog');
    echo json_encode($animal); //["wolf","pig","dog"]
    ?>

    在javascript解析["wolf","pig","dog"]时

    采用:var info = eval(xhr.responseText);语法

    实例3:

    <html>
    <head>
    <title>新建网页</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="description" content="" />
    <meta name="keywords" content="" />
    <script type="text/javascript">
    function f1(){
    //ajax去服务器获得json信息
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
    if(xhr.readyState==4 && xhr.status==200){
    var s = "";
    //alert(xhr.responseText);//数组对象[{"id":1,"name":"xutao","sex":"\u7537","age":30},...]
    var info = eval(xhr.responseText);
    for(var i=0;i<info.length;i++){
    s += info[i].id + "--" + info[i].name + "--" + info[i].sex + "--" + info[i].age +"<br />";
    }
    document.getElementById("user").innerHTML = s;
    }
    }
    xhr.open('get','info.php');
    xhr.send(null);
    }
    </script>
    </head>
    <body>
    <h2>静态网站,javascript对json的接收处理</h2>
    <input type="button" value="触发" onclick="f1()" />
    <div id="user"></div>
    </body>
    </html>
    <?php
    $info = array(
    array("id"=>1,"name"=>"zhangsan","sex"=>"男","age"=>30),
    array("id"=>2,"name"=>"lisi","sex"=>"女","age"=>27),
    array("id"=>3,"name"=>"wangwu","sex"=>"男","age"=>6)
    );
    echo json_encode($info);
    /* [{"id":1,"name":"zhangsan","sex":"\u7537","age":30},
    {"id":2,"name":"lisi","sex":"\u5973","age":27},
    {"id":3,"name":"wuwang","sex":"\u7537","age":6}] */
    ?>

    在javascript解析[{"id":1,"name":"zhangsan","sex":"\u7537","age":30},

    {"id":2,"name":"lisi","sex":"\u5973","age":27},

    {"id":3,"name":"wuwang","sex":"\u7537","age":6}]时

    采用:var info = eval(xhr.responseText);语法

    从数据库读取出来的二维数组,通过json_encode()编码后, 在javascript进行解析时也是采用上述语法。