javaScript系列 [09]-javaScript和JSON (拓展)
JSON搜索
在特定的开发场景中,如果服务器端返回的JSON数据异常复杂(可能超过上万行),那么必然就有对JSON文档进行搜索的需求。在对JSON文档进行搜索的时候,建议使用专业的JSON搜索类库和工具来实现,这可以极大的简化JSON文档搜索的工作并降低工作难度。
JSON搜索的具体适用场景:对于某次API调用,我们只需要其中的部分数据,这种情况我们就可以根据某个标准来对返回的JSON内容进行搜索和过滤。
本文将会先后介绍多款处理JSON搜索的类库(工具),包括但不限于JSONPath
、JSON Pointer
、jq
等,在对各种方案进行介绍的时候将会从方案的优缺点、具体的使用方式等角度切入,开发中可以根据实际的开发场景和各工具自身的特点来进行选择。
工具001 → jq
jq
是一个提供了命令行界面的JSON搜索工具。
功能
可使用自身特定的查询语法来过滤JSON和截取数组,类似于JSON中的sed。
生态
除命令行外,拥有优秀的基于Web的jq测试器,甚至Node社区还以npm模块形式发布了教程。
优势
- 提供丰富的搜索和过滤功能。
- 大多数编程语言都对jq提供良好的支持。
- jq相关文档质量较高,且拥有友好的交互式教程。
- 拥有优秀的在线测试工具,能够对查询提供快速反馈。
- 在命令行中能够很好的与cURL以及管道操作等协同工作。
- 使用C语言编写的,没有运行时依赖,可运行在Linux,OS X和Windows等平台。
资料
官网 、Github仓库 、 Ubuntu-jq手册 、 Hyperpolyglot JSON工具、Jackson类库、Ruby-jq gem
语法
======基本语法======
. 输出所有的文档内容。
| 管道符,传递数据流。
.key 输出指定key对应的部分文档。
.[ ] 输出数组中指定索引对应的元素。
======查询语法示例========
.person[0] 获取JSON文档person数组中的第一个元素。
.person[-1] 获取JSON文档person数组中的最后一个元素。
.person[0:3] 获取JSON文档person数组中的前面三个元素。
.person[] | select (.age>20 ) 获取JSON文档中满足要求(age > 20)的所有数据。
安装
- OSX系统 建议使用Homebrew来安装,具体命令为:
$ brew install jq
- windows系统 建议使用Chocolatey NuGet来安装,具体命令为:
$ chocolatey install jq
- 当然也可以通过
git clone
仓库源码来进行安装,具体细节以及其它系统处理请参考Download jq
这里给出OSX系统中通过命令行安装jq
的具体细节和示例。
wendingding$ brew install jq
Updating Homebrew...
==> Installing dependencies for jq: oniguruma
==> Installing jq dependency: oniguruma
==> Downloading https://homebrew.bintray.com/bottles/oniguruma-6.8.2.high_sierra
######################################################################## 100.0%
==> Pouring oniguruma-6.8.2.high_sierra.bottle.tar.gz
/usr/local/Cellar/oniguruma/6.8.2: 17 files, 1.2MB
==> Installing jq
==> Downloading https://homebrew.bintray.com/bottles/jq-1.5_3.high_sierra.bottle
######################################################################## 100.0%
==> Pouring jq-1.5_3.high_sierra.bottle.tar.gz
/usr/local/Cellar/jq/1.5_3: 19 files, 946.6KB
wendingding$ jq --version
jq-1.5
工具(jq-tutorial)
jq-tutorial是node社区以npm模块的形式发布的jq教程,是学习jq使用的一个比较好用的工具,这里简单列出该模块的安装和使用示例,并对命令进行简单的说明。
wendingding$ npm search jq-tutorial
NAME | DESCRIPTION | AUTHOR | DATE
jq-tutorial | Exercises for… | =rjz | 2016-09-29
wendingding$ npm install -g jq-tutorial
npm WARN notice [SECURITY] lodash has the following vulnerability: 1 low.
Go here for more details: https://nodesecurity.io/advisories?search=lodash version=2.4.2
-Run `npm i npm@latest -g` to upgrade npm version, and then `npm audit` to get more info.
/usr/local/bin/jq-tutorial -> /usr/local/lib/node_modules/jq-tutorial/bin/jq-tutorial
+ jq-tutorial@0.0.5
added 4 packages in 20.012s
wendingding$ jq-tutorial
Run jq-tutorial with one of the following:
* pick
* objects
* mapping
* filtering
* output
* reduce
wendingding$ jq-tutorial pick
Pick
========================================
### Pick fields from an object
`jq` retrieves named properties from objects by using `.` syntax:
$ echo '{"foo": { "bar": "a value" }}' | jq .foo
Nested values are accessible as well:
$ echo '{"foo": { "bar": "a value" }}' | jq .foo.bar
### Pick elements from an array:
Elements in an array may be extracted by index:
$ echo '["snap","crackle","pop"]' | jq .[1]
More than one index? No problem!
$ echo '["snap","crackle","pop"]' | jq .[1, 2]
We can even extract *all* elements at once by omitting the indices:
$ echo '["snap","crackle","pop"]' | jq .[]
type "data?" to see dataset or "help?" for more options
--------------------------------
Given: 'product' (type "data?" to view)
Challenge: Select the entire item (hint: don't overthink this!):
>
工具(jqPlay)
jqPlay是一个基于web的jq在线测试游乐场,它提供了对JSON数据进行jq查询的基本功能,而且提供了简单的jq查询语法示例,能够对查询进行快速反馈。
基本用法演示
这里我先提供一个稍复杂的JSON数据,数据保存在/JSON-Demo/data.json路径
(您可以点击此链接获取该数据)。为了演示方便,这里我将会把该文档的数据部署为RESTful API,从而创建一个模拟的API服务。在具体的处理中,将使用到名为json-server的Node模块,下面列出具体的细节。
wendingding$ pwd
/Users/文顶顶/Desktop/JSON-Demo
wendingding$ npm install -g json-server
/usr/local/bin/json-server -> /usr/local/lib/node_modules/json-server/bin/index.js
+ json-server@0.14.0
added 223 packages in 23.03s
wendingding$ json-server -p 5000 ./data.json
\{^_^}/ hi!
Loading ./data.json
Done
Resources
http://localhost:5000/person
Home
http://localhost:5000
Type s + enter at any time to create a snapshot of the database
GET /person 304 16.355 ms - -
执行$json-server -p 5000 ./data.json
命令之后,我们可以在浏览器中通过http://localhost:5000/person地址来访问JSON文档中的数据,下面是显示结果。
备注:如果经常需要通过浏览器访问和显示JSON数据,建议安装相应的JSON扩展插件,我电脑Chrome安装的是JSON-handle,同类型的还有JSONView。
下面列出jq命令行工具的使用示例以及主要命令行的解读说明。
wendingding$ curl http://localhost:5000/person | jq '.'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1047 100 1047 0 0 169k 0 --:--:-- --:--:-- --:--:-- 255k
[
{
"age": 18,
"name": "mitaoer",
"hasBrother": true,
"email": "mitaoer@126.com",
"interest": [
"文学",
"音乐",
"绘画",
"IT"
],
"car": {
"type": "英菲尼迪",
"number": "京A 000001",
"price": 200012.66
}
},
{
"age": 28,
"name": "wendingding",
"hasBrother": true,
"email": "wenidngding_ios@126.com",
"interest": [
"文学",
"音乐"
],
"car": {
"type": "奥迪",
"number": "京A 000002",
"price": 200000.66
}
},
{
"age": 23,
"name": "xiaxiaoxia",
"hasBrother": true,
"email": "mitaoer@126.com",
"interest": [
"文学",
"IT"
],
"car": null
},
{
"age": 24,
"name": "LiuY",
"hasBrother": true,
"email": "LiuY@126.com",
"interest": [
"文学",
"音乐",
"绘画",
"IT",
"阅读",
"健身"
],
"car": {
"type": "ATS",
"number": "京A 000003",
"price": 888888.66
}
}
]
wendingding$ curl http://localhost:5000/person | jq '.[1]'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1047 100 1047 0 0 140k 0 --:--:-- --:--:-- --:--:-- 255k
{
"age": 28,
"name": "wendingding",
"hasBrother": true,
"email": "wenidngding_ios@126.com",
"interest": [
"文学",
"音乐"
],
"car": {
"type": "奥迪",
"number": "京A 000002",
"price": 200000.66
}
}
wendingding$ curl http://localhost:5000/person | jq '.[1].name'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1047 100 1047 0 0 171k 0 --:--:-- --:--:-- --:--:-- 255k
"wendingding"
wendingding$ curl http://localhost:5000/person | jq '.[1].email'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1047 100 1047 0 0 106k 0 --:--:-- --:--:-- --:--:-- 127k
"wenidngding_ios@126.com"
wendingding$ curl http://localhost:5000/person | jq '.[1] | {name,age}'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1047 100 1047 0 0 161k 0 --:--:-- --:--:-- --:--:-- 204k
{
"name": "wendingding",
"age": 28
}
wendingding$ curl http://localhost:5000/person | jq '.[1] | {newName:.name,newAge:.age}'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1047 100 1047 0 0 153k 0 --:--:-- --:--:-- --:--:-- 204k
{
"newName": "wendingding",
"newAge": 28
}
wendingding$ curl http://localhost:5000/person | jq '.[] | select (.age >=24)'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1047 100 1047 0 0 167k 0 --:--:-- --:--:-- --:--:-- 255k
{
"age": 28,
"name": "wendingding",
"hasBrother": true,
"email": "wenidngding_ios@126.com",
"interest": [
"文学",
"音乐"
],
"car": {
"type": "奥迪",
"number": "京A 000002",
"price": 200000.66
}
}
{
"age": 24,
"name": "LiuY",
"hasBrother": true,
"email": "LiuY@126.com",
"interest": [
"文学",
"音乐",
"绘画",
"IT",
"阅读",
"健身"
],
"car": {
"type": "ATS",
"number": "京A 000003",
"price": 888888.66
}
}
wendingding$ curl http://localhost:5000/person | jq '.[1,3] | {name,email}'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1047 100 1047 0 0 162k 0 --:--:-- --:--:-- --:--:-- 255k
{
"name": "wendingding",
"email": "wenidngding_ios@126.com"
}
{
"name": "LiuY",
"email": "LiuY@126.com"
}
wendingding$ curl http://localhost:5000/person | jq '.[1,3] | [{name,email}]'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1047 100 1047 0 0 165k 0 --:--:-- --:--:-- --:--:-- 255k
[
{
"name": "wendingding",
"email": "wenidngding_ios@126.com"
}
]
[
{
"name": "LiuY",
"email": "LiuY@126.com"
}
]
wendingding$ touch test.json
wendingding$ curl http://localhost:5000/person | jq '.[1,3] | [{name,email}]' > test.json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1047 100 1047 0 0 143k 0 --:--:-- --:--:-- --:--:-- 204k
wendingding$ cat test.json
[
{
"name": "wendingding",
"email": "wenidngding_ios@126.com"
}
]
[
{
"name": "LiuY",
"email": "LiuY@126.com"
}
]
主要命令说明[为方便阅读,命令行中的xxx均代表的是http://localhost:5000/person路径]
$ curl xxx | jq '.'
获取API返回的所有JSON数据。
$ curl xxx | jq '.[1]'
获取JSON文档中person数组的第2个元素内容。
$ curl xxx | jq '.[1].name'
获取JSON文档中person数组的第2个元素(对象)中的name属性值。
$ curl xxx | jq '.[1] | {name,age}'
获取数组第2个元素(对象)的name和age键值对组成新对象。
$ curl xxx | jq '.[1,3] | {name,email}'
获取数组第2和第4个元素中的name和email值组成对象。
$ curl xxx | jq '.[] | select (.age >=24)'
获取JSON文档中所有age属性值>=24的对象元素集合。
补充
如果需要在Node中使用jq,那么可以安装并使用require导入node-jq模块。
工具002 → JSONPath
定位
是一款可用于对JSON文档进行搜索和数据提取操作类库。
历史
由Stefan Goessner于2007年开发,最开始的版本使用JavaScript实现。
功能
能够对JSON文档进行搜索和数据提取,它的查询语法基于XPath实现。
生态
JSONPath没有提供命令行操作实现,但提供了优秀的在线测试工具和Node模块。
优势
- 具有丰富的查询语法。
- 查询语句可以返回文档中的多个元素。
- 大多数的主流平台都对JSONPath提供支持。
- 拥有很高的社区使用率,优秀的在线测试工具对开发者更友好。
资料
jsonpath Node模块 、在线测试网站 、Github仓库、Stefan Goessner主页
语法 | 描述 |
---|---|
$ |
根节点 |
@ |
当前节点的筛选器属性处理 |
* |
通配符,匹配任何属性名称 |
.. |
通配符,匹配任意层次的节点 |
[] |
迭代器标示,同数组索引 |
[,] |
迭代器标示,迭代器多选 |
[start:end] |
数组切片运算符 |
[?(<expression>)] |
过滤表达式,求值必须为布尔值 |
查询语法示例
//备注:参考的JSON数据为前文使用的data.json文件
$ 获取整个JSON文档的内容
$.person 获取JSON文档中person数组的内容
$.person.length 获取JSON文档中person数组的长度(元素个数)
$.person[0] 获取JSON文档中person数组第一个元素的内容
$.person[:1] 获取JSON文档中person数组第一个元素的内容
$.person[-1:] 获取JSON文档中person数组最后一个元素的内容
$.person[(@.length-1)] 获取JSON文档中person数组最后一个元素的内容
$.person[:2] 获取JSON文档中person数组前两个元素的内容
$.person[0,3] 获取JSON文档中person数组第1和4第个元素的内容
$.person[0::2] 获取JSON文档中person数组指定元素的内容(隔一个元素抽取)
$.person[:2].name 获取JSON文档中person数组前两个元素中的name值
$..name 获取JSON文档中所有的name子元素内容
$.person[?(@.age >23)] 获取JSON文档中age值大于23的所有元素
$.person[?(@.age >23)].age 获取JSON文档中age值大于23的所有元素中的age值信息
$.person[?(@.age >20 && @.interest.length == 2)].name 满足多个条件的筛选
工具(jsonpath在线测试网站)
jsonpath提供了对应的在线测试网站,给指定JSON文档输入对应的jsonpath查询语句能够快速看到最终效果。使用该测试工具来可以"重量级的"复杂JSON数据进行快速的筛选,输入查询语句后马上就能够在右侧看到查询后的结果。如果开发者原本不了解查询语法,那也可以通过该工具来快速的学习,下面给出简单的图示。
工具(node模块jsonpath)
JSONPath本身没有命令行工具,除了上面介绍的在线测试网站之外,我们还能在代码中使用node社区发布的jsonpath模块实现JSON的搜索任务。下面给出一个简单的单元测试示例(列出源码和执行情况)。
001 先列出单元测试相关的代码
//jsonpath-test.js文件内容
var unirest = require("unirest");
var jsonPath = require("jsonpath");
var expect = require("chai").expect;
describe("wendingding-test",function(){
var request ;
beforeEach(function(){
request = unirest.get("http://localhost:5000/person")
.header("Accept","application/json");
})
it("return 200 状态码",function(done){
request.end(function(response){
expect(response.statusCode).to.eql(200);
expect(response.headers["content-type"])
.to.eql("application/json; charset=utf-8");
done();
})
})
it("return 所有的JOSN数据",function(done){
request.end(function(response){
expect(response.body.length).to.eql(4);
done();
})
})
it("return 所有的JOSN数据中第一个元素 -- $[1]",function(done){
request.end(function(response){
var resultData = jsonPath.query(response.body,"$[1]");
expect(resultData[0].name).to.eql("wendingding");
done();
})
})
it("return 所有的JOSN数据中最后一个元素[1] -- $[-1:]",function(done){
request.end(function(response){
var resultData = jsonPath.query(response.body,"$[-1:]");
expect(resultData[0].email).to.eql("LiuY@126.com");
done();
})
})
it("return 所有的JOSN数据中最后一个元素[2] -- $[(@.length-1)]",function(done){
request.end(function(response){
var resultData = jsonPath.query(response.body,"$[(@.length-1)]");
expect(resultData[0].email).to.eql("LiuY@126.com");
done();
})
})
it("return 所有的JOSN数据中满足条件元素 -- $[?(@.age >= 23)]",function(done){
request.end(function(response){
var data = response.body;
var resultData = jsonPath.query(data,"$[?(@.age >= 23)]");
//console.log(resultData)
expect(resultData.length).to.eql(3);
for(var i = 0 ;i<resultData.length;i++)
{
expect(resultData[i].age).to.be.at.least(23);
}
done();
})
})
})
002 列出代码的具体执行细节
wendingding$ pwd
/Users/文顶顶/Desktop/jsonPath-demo
wendingding$ npm test
> jsonpath-demo@1.0.0 test /Users/文顶顶/Desktop/jsonPath-demo
> mocha
wendingding-test
✓ return 200 状态码
✓ return 所有的JOSN数据
✓ return 所有的JOSN数据中第一个元素 -- $[1]
✓ return 所有的JOSN数据中最后一个元素[1] -- $[-1:]
✓ return 所有的JOSN数据中最后一个元素[2] -- $[(@.length-1)]
✓ return 所有的JOSN数据中满足条件元素 -- $[?(@.age >= 23)]
6 passing (92ms)
003 代码说明
〇 示例代码中使用了Mocha、Unirest测试框架,jsonpath查询模块以及Chai模块中的断言结构。
① 示例代码中的每一个it
就代表着一个测试用例。
② 示例代码中我们在Mocha的beforeEach()方法
中对请求信息进行了配置。
③ 示例代码中在describe语句定义的范围内,每次执行测试用例之前都会先运行一次beforeEach方法。
004 执行备注
这里简单说明上面代码的执行环境和处理过程。
[1] 在电脑中指定的路径创建文件夹,并通过命令行进入到该路径。
$ mkdir JSON-TEST
$ cd JSON-TEST/
$ pwd
/Users/文顶顶/Desktop/JSON-TEST
[2] 初始化并安装必要的node模块。
$ npm init //默认回车即可
$ npm install -g mocha
$ npm install --save-dev mocha
$ npm install --save-dev unirest
$ npm install -save-dev jsonpath
$ npm install --save-dev chai
[3] 修改package.json文件中的scripts项为"test": "mocha"。
wendingding$ cat package.json
{
"name": "json-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "mocha"
},
"author": "",
"license": "ISC",
"devDependencies": {
"chai": "^4.2.0",
"jsonpath": "^1.0.0",
"mocha": "^5.2.0",
"unirest": "^0.5.1"
}
}
[4] 在当前目录下创建test文件夹,并在该文件夹中创建测试文件(此处命名为json-test.js)。
$ mkdir test
$ cd test/
$ touch json-test.js
[5] 编辑json-test.js文件的内容(前文已经给出),列出文件目录。
.
├── node_modules
│ ├── JSONSelect
│ ...省略
│ ├── chai
│ ├── jsonpath
│ ├── mocha
│ ├── unirest
│ └── xtend
├── package-lock.json
├── package.json
└── test
└── json-test.js
[6] 执行测试。
$ npm test
工具003 → JSON Pointer
JSON Pointer本身是一项用于获取JSON文档中特定值的标准。JSON Pointer设计的主要目标在于支持JSON Schema标准中的$ref(请参考JSON进阶一文)。
目前,大多数的主流平台(包括Node\Ruby on Rails\Ptyhon\Java等)都已经包含JSON Pointer相关的类库。Java,Jackson已支持JSON Pointer查询语法,javaEE 8将提供原生支持;Node中则可以在社区中找到并使用json-pointer模块来对JSON文档进行处理。
语法 | 描述 |
---|---|
/data |
获取JSON文档中某个key对应的所有数据 |
/data/index |
获取指定索引对应的数据 |
/data/index/key |
获取指定索引对应的数据并通过key来取值 |
JSON Pointer的查询语法简洁高效,其工作机制是以/
来表示路径分隔,以索引|下标
来获取指定的内容,索引总是从0开始。下面给出简短的查询语法示例:
//备注:参考的JSON数据为前文使用的data.json文件
/person 获取JSON文档中person的内容
/person/0 获取JSON文档中person数组的第一项内容
/person/0/name 获取JSON文档中person数组的第一项内容中的name值
这里将使用node社区的json-pointer模块简单演示node平台中对JSON文档的处理。下面列出核心单元测试代码(在测试的时候需要先安装对应的模块)。
var expect = require("chai").expect;
var pointer = require("json-pointer");
var unirest = require("unirest");
describe("wendingding-test",function(){
var request ;
beforeEach(function(){
request = unirest.get("http://localhost:5000/person")
.header("Accept","application/json");
})
it("return 200 状态码",function(done){
request.end(function(response){
expect(response.statusCode).to.eql(200);
expect(response.headers["content-type"])
.to.eql("application/json; charset=utf-8");
done();
})
})
it("return 所有的JOSN数据",function(done){
request.end(function(response){
expect(response.body.length).to.eql(4);
done();
})
})
it("return Person数组中的第一个元素 /person/0",function(done){
request.end(function(response){
var resultData = pointer.get(response.body,"/0");
console.log("\n",resultData,"\n");
expect(resultData.name).to.eql("mitaoer");
done();
})
})
it("return Person数组中的第三个元素中interest的值 /person/2/interest",function(done){
request.end(function(response){
var resultData = pointer.get(response.body,"/2/interest");
console.log("\t",resultData);
expect(resultData.length).to.eql(2);
done();
})
})
})
简单列出执行情况:
wendingding$ npm test
> json-pointer-demo@1.0.0 test /Users/文顶顶/Desktop/JSON-Pointer
> mocha
wendingding-test
✓ return 200 状态码
✓ return 所有的JOSN数据
{ age: 18,
name: 'mitaoer',
hasBrother: true,
email: 'mitaoer@126.com',
interest: [ '文学', '音乐', '绘画', 'IT' ],
car: { type: '英菲尼迪', number: '京A 000001', price: 200012.66 }
}
✓ return Person数组中的第一个元素 /person/0
[ '文学', 'IT' ]
✓ return Person数组中的第三个元素中interest的值 /person/2/interest
4 passing (66ms)
JSON转换
JSON → HTML结构(渲染)
将JSON数据转换(处理)为HTML的操作我们应该都很熟悉,在前端开发和移动端开发领域中,这一部分的操作通常和网络请求紧密联系,业务流程基本都是先发请求获取服务器端返回的(JSON)数据,然后通过序列化的方法来对数据进行解析,也就是反序列化处理(通常是将JSON数据转换为编程语言中对应的数据结构比如数组或者是对象),最终再根据得到的数据来更新UI。
现在前端开发中这都属于基本操作,甚至像Vue这样类似的框架中数据绑定
已经是其最最基础的一部分了。虽然如此,为了文章的完整性,这里还是会简单介绍Mustache
和Handlebars
两个类库在JSON转换中的运用。
Mustache
简介
Mustache使用声明式模板来转换数据格。
资料
Mustache、Mustache Github、Mustache 5说明文档
优势
使用模板可以从代码中抽取具体的数据信息,并将数据保存在外部的文件中,实现关注点分离。
接下来我将通过一个简短的示例来说明Mustache的语法以及其使用方式,您可以点击该链接获取完整的项目内容。为了对介绍Mustache的使用,这里我列出项目中的部分内容并做简要说明。
001 列出模板核心内容
//备注:../Tem-TEST/src/index.mustache文件的核心内容
<div id="app">
<table id="tb">
<tr>
<th>name</th>
<th>age</th>
<th>email</th>
<th>interest</th>
<th>car-type</th>
<th>car-number</th>
</tr>
{{#person}}
<tr>
<td>{{name}}</td>
<td>{{age}}</td>
<td>{{email}}</td>
<td>{{interest.1}}</td>
{{#car}}
<td>{{type}}</td>
<td>{{number}}</td>
{{/car}}
</tr>
{{/person}}
</table>
</div>
002 列出单元测试代码
//备注:../Tem-TEST/test/mustache-test.js文件内容
var fs = require("fs");
var expect = require("chai").expect;
var jsonfile = require("jsonfile");
var mustache = require("mustache");
describe("wendingding-mustache-test",function(){
//文件的目录结构:json文件路径 + 模板文件路径 + 目标文件路径
var jsonFileFullPath = __dirname + "/../src/data.json";
var templateFileFullPath = __dirname + "/../src/index.mustache";
var targetFileFunllPath = __dirname + "/../src/index.html";
it("JSON -> HTML",function(done){
jsonfile.readFile(jsonFileFullPath,function(readJsonFileError,jsonData){
if(!readJsonFileError)
{
fs.readFile(templateFileFullPath,"utf8",function(readTemplateFileError,templateData){
if(!readTemplateFileError)
{
var template = templateData.toString();
var html = mustache.render(template,jsonData);
fs.writeFile(targetFileFunllPath, html, function(errorStatus) {
if (!errorStatus) {
console.log("转换成功并保存为HTML文件!");
done();
}else
{
done(errorStatus);
}
});
}else
{
done(readTemplateFileError)
}
})
}else
{
done(readJsonFileError)
}
})
})
})
通过$ npm test
执行单元测试代码,将会执行JSON数组到HTML的转换,处理完毕后结果保存到index.html文件中,下面贴出该页面的效果图。
Mustache模板工作机制
❐ 模板基于HTML, Mustache使用JSON数据来解析标签。
❐ 模板中的标签可以表示单个字段,使用双大括号
的形式来包裹。
❐ 模板中的区块都需要由开始标签和结尾标签
组成,例如上例中的person。
❐ 模板中的区块对应JSON数据中的数组或者是对象,例如上例中的person和car。
❐ 模板中的区块可以为内部的标签定义上下文,比如car区块内部的type和number。
Mustache工具(命令行 && 在线网站)
Mustache除上面演示的使用方式之外,还能直接在命令行中使用,下面给出简短的使用示例。
(1) 全局安装Mustache模块。
$ npm install -g mustache
(2) 执行mustache命令转换。
$ mustache /Users/文顶顶/Desktop/Tem-TEST/src/data.json /Users/文顶顶/Desktop/Tem-TEST/src/index.mustache > target.html
$ open target.html
这里再推荐一款好用的在线模板编辑器Architect,使用该工具可以有效的简化测试和开发模板的工作,当修改模板的时候,可以实时的看到渲染的结果。该网站支持多款主流模板引擎(包括doT.js、Dustjs、EJS、Handlebars.js、Hogan.js、Jade、Mustache、Nunjucks和Underscore.js等)的编辑和渲染,可以有效的加速开发和调试工作。
Handlebars
简介
Handlebars是Mustache的扩展,使用hash或对象来渲染模板中的标签。
资料
Handlebars、Handlebars Github、Architect、在线测试网站、Handlebars的Node模块
说明
Handlebars与Mustache高度兼容,相对而言Handlebars自身增加了一些特性来增强转换操作。它们的差异主要在于Handlebars提供了if
和unless
等内联的辅助语句且允许开发者通过注册自定义辅助语义的方式来进行扩展,功能更加强大。
优势
- 模板语言丰富,能满足大多数的转换需求。
- 拥有优秀的在线编辑和测试工具用起来更加方便。
- 采用声明式,但也支持在自定义辅助指令中编写逻辑代码。
- 因为拥有内置的条件逻辑,所以在渲染的时候几乎可以不用编写额外的处理代码。
- 跨平台的支持度很好,支持
JavaScript
、Node.js
、Java
和Ruby on Rails
等平台。
这里将简单介绍Handlebars在Node中的使用,并提供node和命令行两种执行示例供参考,更多的细节请自行参考其官网文档。
001 列出模板文件的核心代码
//备注(1):/Users/文顶顶/Desktop/Handlebars-Test/index.hbs文件的核心内容
//备注(2):转换过程中使用的json数据为前文中的data.json文件
<div id="app">
<table id="tb">
<tr>
<th>name</th>
<th>age</th>
<th>email</th>
<th>car-type</th>
<th>car-Other</th>
</tr>
{{#each person}}
{{#if car}}
<tr>
<td>{{name}}</td>
<td>{{age}}</td>
<td>{{email}}</td>
<td>{{car.type}}</td>
<td>号码:{{car.number}} | 价格:{{car.price}}</td>
</tr>
{{/if}}
{{/each}}
</table>
</div>
002 列出命令行执行的细节
(1) 先通过命令行全局安装hb-interpolate模块
$ npm install -g hb-interpolate
(2) 执行渲染命令。
$ hb-interpolate -j /Users/文顶顶/Desktop/Handlebars-Test/data.json -t /Users/文顶顶/Desktop/Handlebars-Test/index.hbs > target.html
说明 上面的命令行中-j
表示后面跟的是json文件,-t
表示后面跟的是模板文件,转换后的结果被输出并保存到target.html文件中。在模板文件中的#each
表示遍历数组,#if
是逻辑控制指令,在渲染的时候过滤了car为null的数据情况,浏览器打开target.html文件可以看到下面的显示结果。
003 Handlebars在Node中的使用
//备注:handlebars-test.js文件的内容
//001 导入node模板
var fs = require("fs");
var jsonfile = require("jsonfile");
var handlebars = require("handlebars");
//002 处理文件路径
var jsonFullPath = __dirname + "/data.json";
var templateFullPath = __dirname + "/index.hbs";
var outPutFullPath = __dirname + "/output.html";
//003 读取JSON文件的内容
jsonfile.readFile(jsonFullPath,function(readJsonError,jsonData){
if(!readJsonError)
{
//004 读取模板文件的内容
fs.readFile(templateFullPath,"utf8",function(readTemplateError,templateData){
if(!readTemplateError)
{
//005 JSON数据 + 模板 = > 渲染
var template = handlebars.compile(templateData);
var html = template(jsonData);
//006 把渲染后的结果保存到指定文件中
fs.writeFile(outPutFullPath, html, function(errorStatus) {
if(! errorStatus)
{
console.log("渲染成功!请打开"+outPutFullPath+"查看结果!");
}
})
}
})
}
})
说明 在指定文件目录中(我这里是/Users/文顶顶/Desktop/Handlebars-Test
)创建handlebars-test.js文件,并编写上述对应的代码。通过命令行安装必要的Node模块,执行即可得到前文所示的图片结果。下面给出命令行执行的细节:
(1) 切换到当前目录
$ cd Handlebars-Test/
$ pwd
/Users/文顶顶/Desktop/Handlebars-Test
(2) 使用npm初始化并安装必要的Node模块。
$ npm init
$ npm install --save-dev jsonfile
$ npm install --save-dev handlebars
(3) 执行。
$ node handlebars-test.js
补充 Mustache和Handlebars除用来把JSON转换为HTML(渲染)之外,还能够对JSON数据本身的格式进行转换工作(主要是对JSON数据进行二次处理,譬如删减或结构调整等),但Mustache在具体进行格式化的时候因为无法确定当前所处理的元素是否为数组或对象的最后一个元素,所以可能存在"无谓逗号"的问题。Handlebars可以通过使用#unless 和@last
的形式对"无谓逗号"的问题进行规避,具体的细节请参考其官方文档说明。另外,JSON格式转换的工具在Node环境中推荐使用Json2Json和jsonapter,也可以参考JSON Patch和JSON-T的实现,这里不再展开。
JSON数据 ←→ XML文档
最后简单介绍JSON数据和XML数据之间的相互转换,虽然这种场景通常可能很少出现(因为开发中常见的场景一般是对JSON或XML数据进行序列化或反序列化处理,JSON和XML两种数据格式之间直接相互转换的情况真的很少见)。
XML全称Extensible Markup Language
(可扩展标记语言),主要流行于(1998~2008年)。同JSON类似,XML也能用于表示和传输数据,现在很多大公司的API都提供XML和JSON两种格式的数据响应。
XML数据和JSON数据的转换难易程度主要看XML文档的结构,如果文档中所有的数据都以XML元素和文本的方式保存那么转换为JSON数据是比较简单的,如果XML文档的元素上存在这大量的属性节点(早年的时候很多XML的设计人员把数据保存在XML的属性节点上,这样有助于减少文件体积和简化多平台之间的转换工作),那么这种转换就会比较困难。不过,好在我们可以使用很多现成的工具(譬如:Parker和JsonML以及Badgerfish等)来完成这种具体的转换工作。
对于上面这些工具的具体使用情况,大家可以自行了解。需要说明的是即便如此,这些工具仍然存在很大的局限性(譬如文档不全
、缺乏跨平台的支持
和完整实现
以及有损转换
等等)。所以,在实际的使用过程中其实可以考虑先把JSON|XML转换为当前编程语言中的数据结构形式,然后再转换成XML|JSON)。下面以JavaScript(Node)平台为例加以简单说明。
XML文档 → JSON数据
- 先把XML数据解析为JavaScript中的对象|数组(
xml2js模块
)。 - 把JavaScript的对象|数组序列化为JSON格式的数据(
JSON.stringify方法
)。
列出核心示例代码
var xmlFullPath = __dirname + "/data.xml";
fs.readFile(xmlFullPath,"utf8",function(readFileError,xmlData){
var parser = new xml2js.Parser();
parser.parseString(xmlData,function(error,xmlObj){
console.log(JSON.stringify(xmlObj,null,2));
})
})
JSON数据 → XML文档
- 先将JSON数据解析(反序列化)为JavaScript中的对象|数组数据(
eval函数或者是JSON.parse方法
)。 - 根据JavaScript数据来生成(marshaling)对应的XML文档(
xml2js模块
)
这里列出json数据转换为xml数据的代码示例。
//001 导入node模板
var fs = require("fs");
var xml2js = require("xml2js");
var jsonfile = require("jsonfile");
//002 处理文件路径
var jsonFullPath = __dirname + "/data.json";
var xmlFullPath = __dirname + "/data.xml";
//003 读取JSON文件的内容并解析为JavaScript对象(jsonData)
jsonfile.readFile(jsonFullPath,function(readJsonError,jsonData){
if(!readJsonError)
{
//004 创建并返回bulider实例对象
var bulider = new xml2js.Builder();
//005 使用bulider对象将jsonData转换为xml格式的字符串
var xml = bulider.buildObject(jsonData);
//006 写文件操作(把最终的数据保存到指定的文件中)
fs.writeFile(xmlFullPath, xml, function(errorStatus) {
if(! errorStatus)
{
console.log("json->XML 转换成功");
console.log(xml);
}
})
}
})
列出用于转换的初始json数据。
{
"person": [
{
"age": 18,
"name": "mitaoer",
"hasBrother": true,
"email": "mitaoer@126.com",
"car": {
"type": "英菲尼迪",
"number": "京A 000001",
"price": 200012.66
}
},
{
"age": 28,
"name": "wendingding",
"hasBrother": true,
"email": "wenidngding_ios@126.com",
"car": {
"type": "奥迪",
"number": "京A 000002",
"price": 200000.66
}
}
]
}
列出最终输出的xml数据内容。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
<age>18</age>
<name>mitaoer</name>
<hasBrother>true</hasBrother>
<email>mitaoer@126.com</email>
<car>
<type>英菲尼迪</type>
<number>京A 000001</number>
<price>200012.66</price>
</car>
<age>28</age>
<name>wendingding</name>
<hasBrother>true</hasBrother>
<email>wenidngding_ios@126.com</email>
<car>
<type>奥迪</type>
<number>京A 000002</number>
<price>200000.66</price>
</car>
</person>
javaScript系列 [09]-javaScript和JSON (拓展)的更多相关文章
- javaScript系列 [06]-javaScript和this
在javaScript系列 [01]-javaScript函数基础这篇文章中我已经简单介绍了JavaScript语言在函数使用中this的指向问题,虽然篇幅不长,但其实最重要的部分已经讲清楚了,这篇文 ...
- javaScript系列 [03]-javaScript原型对象
[03]-javaScript原型对象 引用: javaScript是一门基于原型的语言,它允许对象通过原型链引用另一个对象来构建对象中的复杂性,JavaScript使用原型链这种机制来实现动态代理. ...
- (一)我的Javascript系列:Javascript的面向对象旅程(上)
今宵酒醒何处,杨柳岸,晓风残月 导引 我的JavaScript系列文章是我自己对JavaScript语言的感悟所撰写的系列文章.现在还没有写完.目前一共出了下面的系列: (三)我的JavaScript ...
- javaScript系列 [05]-javaScript和JSON
本文输出和JSON有关的以下内容❐ JSON和javaScript❐ JSON的语法介绍❐ JSON的数据类型❐ JSON和XMLHTTPRequest❐ JSON的序列化和反序列化处理 1.1 JS ...
- (五)我的JavaScript系列:JavaScript的糟粕
泪眼问花花不语,乱红飞过秋千去. JavaScript的糟粕 JavaScript语言是一门集精华与糟粕于一体的语言.在JavaScript: the good parts中,便集中讨论了关于精华与糟 ...
- javascript系列-Class1.JavaScript基础
欢迎加入前端交流群来py:749539640 转载请标明出处! JavaScript概述 一个页面分成三个部分,结构,样式,行为. HTML代表了页面的结 ...
- 深入理解JavaScript系列:JavaScript的构成
此篇文章不是干货类型,也算不上概念阐述,就是简单的进行一个思路上的整理. 要了解一样东西或者完成一件事情,首要的就是先要搞清楚他是什么.作为一个前端开发人员,JavaScript应该算作是最核心之一的 ...
- 【详解JavaScript系列】JavaScript之变量
一 概述 本篇文章将讲解JavaScript中的变量,大致内容归结为: 1.变量定义 包括变量声明和变量初始化 2.变量种类 包括局部变量和全局变量 3.变量链式作用域及访问 二 内容 (一)变量 ...
- javaScript系列 [01]-javaScript函数基础
[01]-javaScript函数基础 1.1 函数的创建和结构 函数的定义:函数是JavaScript的基础模块单元,包含一组语句,用于代码复用.信息隐蔽和组合调用. 函数的创建:在javaScri ...
随机推荐
- 【swift】CoreData Crash(崩溃)(Failed to call designated initializer on NSManagedObject class)
感谢另一篇博客:https://blog.csdn.net/devday/article/details/6577985 里面的图片和介绍,发现问题如他描述的一样,没有bundle 我的Xcode版本 ...
- mysql 索引 零记
索引算法 二分查找法/折半查找法 伪算法 : 1. 前提,数据需要有序 2. 确定数据中间元素 K 3. 比如目标元素 A与K的大小 3.1 相等则找到 3.2 小于时在左区间 3.3 大于时在右 ...
- Project Reactor工厂方法和错误处理
工厂方法创建流 Backpressure : the ability for the consumer to signal the producer that the rate of emission ...
- Docker(4)-docker常用命令
帮助命令 docker version # 查看docker的版本信息 docker info # 查看docker的系统信息,包含镜像和容器的数量 docker --help # 帮助命令 dock ...
- HCL华三模拟器静态路由实验
(copy自我的其他博客网站) 拓扑如下: 实验目的:通过给A.B.C三台路由器配置静态路由,使PC1可以ping通PC2. 实验环境:Windows10 (21H1),HCL(V3.0.1) 实验步 ...
- MySQL 面试题汇总(持续更新中)
COUNT COUNT(*) 和 COUNT(1) 根据 MySQL 官方文档的描述: InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) opera ...
- Linux 01 计算机硬件之冯诺依曼体系
1. 计算机硬件软件体系 1.1 冯诺依曼体系结构 (1) 计算机处理的数据和指令用二进制表示 (2) 按顺序执行指令 (3) 计算机硬件:运算器.控制器.储存器.输入设备和输出设备. 1.2 计算机 ...
- CF1082A Vasya and Book 题解
Content 给定 \(T\) 组数据,每组数据给出四个整数 \(n,x,y,d\).小 V 有一本 \(n\) 页的书,每次可以恰好翻 \(d\) 页,求从第 \(x\) 页恰好翻到第 \(y\) ...
- linux下记录入站请求
将内网机器通过frp映射到公网后,内网主机受到大量ssh爆破攻击,攻击来源为frp的服务端,仅在内网机器上无法追踪到真实的攻击来源的ip.下面记录了在frp服务端监控指定端口的入站数据,找到真正的攻击 ...
- CentOS7下使用NFS文件共享给Window server 2012
CentOS7下使用NFS文件共享给Window server 2012 2018年08月24日 23:15:54 疼迅扣扣 阅读数:443 出自https://blog.csdn.net/u013 ...