• Posts tagged "JSON"

Blog Archives

restify构建REST服务

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-restify/

nodejs-restify

前言

随着互联网应用的兴起,web2.0时代的到来,越来越多的人,选择用REST编程来代替原来的页面渲染。REST以资源为中心的web服务,分离了展现层和服务层,让前端和后端程序员能更专注于自己擅长的领域。

restify让REST变得如此简单!

目录

  1. 什么是REST?
  2. restify介绍
  3. restify安装
  4. restify服务端API
  5. restify客户端API

1. 什么是REST?

REST(Representational State Transfer表述性状态转移)是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。

REST 定义了一组体系架构原则,您可以根据这些原则设计以系统资源为中心的 Web 服务,包括使用不同语言编写的客户端如何通过 HTTP 处理和传输资源状态。 如果考虑使用它的 Web 服务的数量,REST 近年来已经成为最主要的 Web 服务设计模式。 事实上,REST 对 Web 的影响非常大,由于其使用相当方便,已经普遍地取代了基于 SOAP 和 WSDL 的接口设计。

REST中的资源所指的不是数据,而是数据和表现形式的组合,比如“最新访问的10位会员”和“最活跃的10位会员”在数据上可能有重叠或者完全相同,而由于他们的表现形式不同,所以被归为不同的资源,这也就是为什么REST的全名是Representational State Transfer的原因。资源标识符就是URI(Uniform Resource Identifier),不管是图片,Word还是视频文件,甚至只是一种虚拟的服务,也不管你是xml格式、txt文件格式还是其它文件格式,全部通过 URI对资源进行唯一的标识。

文字介绍,摘自: http://baike.baidu.com/view/1077487.htm

2. restify介绍

restify是一个基于Nodejs的REST应用框架,支持服务器端和客户端。restify比起express更专注于REST服务,去掉了express中的template, render等功能,同时强化了REST协议使用,版本化支持,HTTP的异常处理。

restify提供了DTrace功能,为程序调式带来新的便利!

restifty的发布页:http://mcavage.me/node-restify/

3. restify安装

系统环境

  • Linux: Ubuntu 12.04 LTS 64bit
  • node: v0.6.12
  • npm: 1.1.4

创建项目


~ cd /home/conan/nodejs
~ mkdir nodejs-restify && cd nodejs-restify

~ sudo npm install restify
restify@2.6.1 node_modules/restify
├── assert-plus@0.1.4
├── once@1.3.0
├── deep-equal@0.0.0
├── escape-regexp-component@1.0.2
├── qs@0.6.5
├── tunnel-agent@0.3.0
├── keep-alive-agent@0.0.1
├── lru-cache@2.3.1
├── node-uuid@1.4.0
├── negotiator@0.3.0
├── mime@1.2.11
├── semver@2.2.1
├── spdy@1.14.12
├── backoff@2.3.0
├── formidable@1.0.14
├── csv@0.3.6
├── dtrace-provider@0.2.8
├── verror@1.3.6 (extsprintf@1.0.2)
├── bunyan@0.22.0 (mv@0.0.5)
└── http-signature@0.10.0 (assert-plus@0.1.2, asn1@0.1.11,ctype@0.5.2)

创建一个简单的rest服务

新建文件:app.js


~ vi app.js

var restify = require('restify');

function respond(req, res, next) {
  res.send('hello ' + req.params.name);
}

var server = restify.createServer();
server.get('/hello/:name', respond);
server.head('/hello/:name', respond);

server.listen(3900, function() {
  console.log('%s listening at %s', server.name, server.url);
});

运行程序


~ node app.js
restify listening at http://0.0.0.0:3900

使用curl访问测试


#空路径
~ curl localhost:3900
{"code":"ResourceNotFound","message":"/ does not exist"

#正常GET请求
~ curl localhost:3900/hello/conan
"hello conan"

#设置content-type, 返回包括header信息
~ curl -i localhost:3900/hello/conan -H 'accept:text/plain'
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 11
Date: Mon, 13 Jan 2014 08:31:30 GMT
Connection: keep-alive

hello conan

#设置connection:close, 返回包括header信息
~  curl -i localhost:3900/hello/conan -H 'connection:close'
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 13
Date: Mon, 13 Jan 2014 08:29:38 GMT
Connection: close

hello conan

我们看到用restify创建REST服务非常的容易,接下来我们将深入学习restify的API。

4. restify服务端API

  • restify.createServer(): 创建服务器
  • server.use(): 注册handler
  • server.get(): 路由控制
  • server.formatters: 设置content-type
  • 异常处理
  • socket.IO集成/li>
  • Server API
  • 组件管理
  • Request API
  • Response API

1). restify.createServer(): 创建服务器

创建服务器代码


var restify = require('restify');

var server = restify.createServer({
  certificate: ...,
  key: ...,
  name: 'MyApp',
});

server.listen(8080);

服务器创建参数

  • certificate: string, HTTPS服务器的证书
  • key: string, HTTPS服务器的证书key
  • formatters: object, 自定义的response的content-type
  • log: object, 服务器日志,可以配合bunyan一起使用
  • name: string, 服务器的response header
  • spdy: Object, 允许集成node-spdy服务器
  • version: string, 路由版本
  • responseTimeHeader: string, X-Response-Time
  • responseTimeFormatter: function, 格式化header的值

2). server.use(): 注册handler

注册服务器控制组件,按照代码顺序执行,需要放在路由代码之前。

3). server.get(): 路由控制

REST响应G请求:

  • server.get(): 响应GET请求
  • server.post(): 响应POST请求
  • server.put(): 响应PUT请求
  • server.head(): 响应HEAD请求
  • server.del(): 响应DELETE请求

路由控制代码


 function send(req, res, next) {
   res.send('hello ' + req.params.name);
   return next();
 }

 server.post('/hello', function create(req, res, next) {
   res.send(201, Math.random().toString(36).substr(3, 8));
   return next();
 });
 server.put('/hello', send);
 server.get(/^\/([a-zA-Z0-9_\.~-]+)\/(.*)/, send);
 server.head('/hello/:name', send);
 server.del('hello/:name', function rm(req, res, next) {
   res.send(204);
   return next();
 });

4). server.formatters: 设置Response的content-type

content-type类型

  • application/json
  • text/plain
  • application/octet-stream

增加自定义的content-type:application/foo


var server = restify.createServer({
  formatters: {
    'application/foo': function formatFoo(req, res, body) {
      if (body instanceof Error)
        return body.stack;

      if (Buffer.isBuffer(body))
        return body.toString('base64');

      return util.inspect(body);
    }
  }
});

显式的设置Response的content-type


res.setHeader('content-type', 'application/foo');
res.send({hello: 'world'});

5). 异常处理

异常处理代码


var server = restify.createServer();
server.get('/hello/:name', function(req, res, next) {
  return next(new restify.InvalidArgumentError("I just don't like you"));
});

运行程序


~ curl -i localhost:3900/err/conan

HTTP/1.1 409 Conflict
Content-Type: application/json
Content-Length: 60
Date: Mon, 13 Jan 2014 11:06:26 GMT
Connection: keep-alive

RestError的内置的异常类型:

  • BadDigestError: 400
  • BadMethodError: 405
  • InternalError: 500
  • InvalidArgumentError: 409
  • InvalidContentError: 400
  • InvalidCredentialsError: 401
  • InvalidHeaderError: 400
  • InvalidVersionError: 400
  • MissingParameterError: 409
  • NotAuthorizedError: 403
  • RequestExpiredError: 400
  • RequestThrottledError: 429
  • ResourceNotFoundError: 404
  • WrongAcceptError: 406

自定义异常:MyError, errorCode:418


var restify = require('restify');
var util = require('util');

function MyError(message) {
  restify.RestError.call(this, {
    restCode: 'MyError'
    statusCode: 418,
    message: message,
    constructorOpt: MyError
  });
  this.name = 'MyError';
};
util.inherits(MyError, restify.RestError);

6). Socket.IO集成

服务器端集成代码


var server = restify.createServer();
var io = socketio.listen(server);

server.get('/', function indexHTML(req, res, next) {
    fs.readFile(__dirname + '/index.html', function (err, data) {
        if (err) {
            next(err);
            return;
        }

        res.setHeader('Content-Type', 'text/html');
        res.writeHead(200);
        res.end(data);
        next();
});


io.sockets.on('connection', function (socket) {
    socket.emit('news', { hello: 'world' });
    socket.on('my other event', function (data) {
            console.log(data);
    });
});

server.listen(8080, function () {
    console.log('socket.io server listening at %s', server.url);
});

7). Server API

Events事件监听:

  • NotFound: 404 handler
  • MethodNotAllowed: 405 handler
  • VersionNotAllowed: 400 handler
  • UnsupportedMediaType: 415 handler
  • after: 在所有handler之后执行
  • uncaughtException: 未处理的异常

注: uncaughtException的处理,可以参考文章, Nodejs异步异常处理domain

Properties配置属性:

  • name: string, 服务器名字
  • version: string, 路由默认版本
  • log: Object, 日志对象
  • acceptable: Array(String), content-types列表
  • url: string, 服务器信息

Methods函数:

  • address(): 绑定地址
  • listen(port, [host], [callback]): 启动服务器
  • close(): 停止服务器
  • pre(): 在路由之前触发的组件
  • use(): 注册组件

8). 组件管理

restify已支持的组件

  • Accept header parsing: 解析aceept header,返回客户端
  • Authorization header parsing: HTTP Basic Auth认证
  • Date header parsing: 数据头解析
  • JSONP support: JSONP请求
  • Gzip Response: 设置accept-encoding:gzip
  • Query string parsing: 解析URL参数
  • Body parsing (JSON/URL-encoded/multipart form): 解析内容
  • Static file serving: 静态文件处理
  • Throttling: 优化服务器性能配置
  • Conditional request handling: 设置请求条件
  • Audit logger: 日志记录

注册组件server.use()


var server = restify.createServer();
server.use(restify.acceptParser(server.acceptable));
server.use(restify.authorizationParser());
server.use(restify.dateParser());
server.use(restify.queryParser());
server.use(restify.jsonp());
server.use(restify.gzipResponse());
server.use(restify.bodyParser());
server.get(/\/docs\/public\/?.*/, restify.serveStatic({
  directory: './public'
}));
server.use(restify.throttle({
  burst: 100,
  rate: 50,
  ip: true,
  overrides: {
    '192.168.1.1': {
      rate: 0,        // unlimited
      burst: 0
    }
  }
}));
server.use(function setETag(req, res, next) {
  res.header('ETag', 'myETag');
  res.header('Last-Modified', new Date());
});
server.use(restify.conditionalRequest());
server.on('after', restify.auditLogger({
  log: bunyan.createLogger({
    name: 'audit',
    stream: process.stdout
  })
}));

9). Request API

对node内核API:http.ServerRequest的封装

10). Response API

对node内核API:http.ServerResponse的封装

5. restify客户端API

  • JsonClient: 收application/json, 发application/json
  • StringClient: 收text/plain, 发url-encoded request
  • HttpClient: 封装http/https

新建服务器端测试程序


var restify = require('restify');
var server = restify.createServer();
server.use(restify.acceptParser(server.acceptable));
server.use(restify.authorizationParser());
server.use(restify.dateParser());
server.use(restify.queryParser());
server.use(restify.jsonp());
server.use(restify.gzipResponse());
server.use(restify.bodyParser());

# 用于处理JsonClient请求
server.get('/json/v1',function(req,res,next){
  var a = {name:'conan',blog:'blog.fens.me'}
  res.send(a);
});

# 用于处理StringClient请求
server.get('/json/v2',function(req,res,next){
  var a = {name:'conan',blog:'blog.fens.me'}
  res.send(JSON.stringify(a));
});


server.listen(3900, function() {
  console.log('%s listening at %s', server.name, server.url);
});

1). JsonClient

curl请求


~ curl -i http://localhost:3900/json/v1

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 38
Date: Mon, 13 Jan 2014 12:09:36 GMT
Connection: keep-alive

{"name":"conan","blog":"blog.fens.me"}

创建文件:jsonclient.js


~ vi jsonclient.js

var restify = require('restify');

var client = restify.createJsonClient({
  url: 'http://localhost:3900'
});

client.get('/json/v1', function(err, req, res, obj) {
  if(err) console.log(err)
  console.log(JSON.stringify(obj, null, 2));
});

运行程序:


~ node jsonclient.js
{
  "name": "conan",
  "blog": "blog.fens.me"
}

2). StringClient

curl请求


~ curl -i http://localhost:3900/json/v2 -H 'accept:text/plain'

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 38
Date: Mon, 13 Jan 2014 12:10:07 GMT
Connection: keep-alive

{"name":"conan","blog":"blog.fens.me"}

创建文件:stringclient.js


~ vi stringclient.js

var restify = require('restify');

var client = restify.createStringClient({
  url: 'http://localhost:3900'
});

client.get('/json/v2', function(err, req, res, obj) {
  if(err) console.log(err)
  console.log(JSON.stringify(obj, null, 2));
});

运行程序:


~ node stringclient.js
"{\"name\":\"conan\",\"blog\":\"blog.fens.me\"}"

3). HttpClient

创建文件: httpclient.js


~ vi httpclient.js

var restify = require('restify');

var client = restify.createClient({
  url: 'http://localhost:3900'
});

client.get('/json/v1', function(err, req) {

  req.on('result', function(err, res) {
    res.body = '';
    res.setEncoding('utf8');
    res.on('data', function(chunk) {
      res.body += chunk;
    });

    res.on('end', function() {
      console.log(res.body);
    });
});
});

运行程序


~ node httpclient.js
{"name":"conan","blog":"blog.fens.me"}

我们已经全面了解的了restify包,接下就可以快速构建我们自己的REST服务了。Node实现如此之简单,让原来Java程序员如何生存呢?!

转载请注明出处:
http://blog.fens.me/nodejs-restify/

打赏作者

R和JSON的傻瓜式编程

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-json-rjson/

rjson

前言

JSON作为一种轻量级数据格式,被大量地应用在各种程序环境中。JSON(JavaScript Object Notation)是Javascript的内嵌的标准对象,同时也是MongoDB的表结构存储类型。JSON是半结构化的,可以表达出丰富的文档含义。JSON文档比XML文档要少很多,更适合于网络传输。

早期R语言编程很少会用到JSON,但随着R语言的壮大,R也在伸向各种领域,JSON就是与其他领域的一个交点。

如何让R语言傻瓜式花转JSON?请看正文介绍。

目录

  1. rjson包介绍
  2. RJSONIO包介绍
  3. 自定义JSON的实现
  4. toJSON性能比较: 我的测试结果rjson比RJSONIO更高效

1. rjson包介绍

rjson是一个R语言与json进行转的包,是一个非常简单的包,支持用 C类库转型和R语言本身转型两种方式。

rjson库,提供的函数只有3个,fromJSON(), newJSONParser(), toJSON()。 下面我们将介绍rjson库如何使用。

我的系统环境

  • Win7: x86_64-w64-mingw32/x64 (64-bit)
  • R: version 3.0.1 (2013-05-16)

1). 安装和加载rjson


install.packages("rjson")
library(rjson)

新建一个JSON文件: fin0.json


~ vi fin0.json

{
    "table1": {
        "time": "130911",
        "data": {
            "code": [
                "TF1312",
                "TF1403",
                "TF1406"
            ],
            "rt_time": [
                130911,
                130911,
                130911
            ]
        }
    },
    "table2": {
        "time": "130911",
        "data": {
            "contract": [
                "TF1312",
                "TF1312",
                "TF1403"
            ],
            "jtid": [
                99,
                65,
                21
            ]
        }
    }
}

2). fromJSON() 从JSON到R
从fin0.json文件中,读取JSON并解析为R语言对象。


> json_data <- fromJSON(paste(readLines("fin0.json"), collapse=""))
> json_data
$table1
$table1$time
[1] "130911"

$table1$data
$table1$data$code
[1] "TF1312" "TF1403" "TF1406"

$table1$data$rt_time
[1] 130911 130911 130911

$table2
$table2$time
[1] "130911"

$table2$data
$table2$data$contract
[1] "TF1312" "TF1312" "TF1403"

$table2$data$jtid
[1] 99 65 21

我们看到原JSON,除最内层都被解析为R的list类型,最内层则是向量类型。

在R对象结构中取叶子节点, json.table1.data.code[0]


> json_data$table1$data$code
[1] "TF1312" "TF1403" "TF1406"

> json_data$table1$data$code[1]
[1] "TF1312"

3). toJSON() 从R到JSON
把R对象序列化为JSON串,还以刚才的json_data为例


> json_str<-toJSON(json_data)

> print(json_str)
[1] "{\"table1\":{\"time\":\"130911\",\"data\":{\"code\":[\"TF1312\",\"TF1403\",\"TF1406\"],\"rt_time\":[130911,130911,130911]}},\"table2\":{\"time\":\"130911\",\"data\":{\"contract\":[\"TF1312\",\"TF1312\",\"TF1403\"],\"jtid\":[99,65,21]}}}"

> cat(json_str)
{"table1":{"time":"130911","data":{"code":["TF1312","TF1403","TF1406"],"rt_time":[130911,130911,130911]}},"table2":{"time":"130911","data":{"contract":["TF1312","TF1312","TF1403"],"jtid":[99,65,21]}}}

我们只要使用toJSON()函数,就可以实现R对象向JSON的转成。

如果用print结果就是带转义的输出(\”),直接用cat打印就是标准的JSON串格式。

把JSON输出到文件:fin0_out.json, 有2种方法:

  • 1. writeLines()
  • 2. sink()

# writeLines
> writeLines(json_str, "fin0_out1.json")

# sink
> sink("fin0_out2.json")
> cat(json_str)
> sink()

虽然写法不同,输出结果是一个样的,writeLines最后新建一个空行。


{"table1":{"time":"130911","data":{"code":["TF1312","TF1403","TF1406"],"rt_time":[130911,130911,130911]}},"table2":{"time":"130911","data":{"contract":["TF1312","TF1312","TF1403"],"jtid":[99,65,21]}}}

4). C库和R库转型 性能测试
我们对fromJSON进行性能测试


> system.time( y <- fromJSON(json_str,method="C") )
用户 系统 流逝 
   0    0    0 
> system.time( y2 <- fromJSON(json_str,method = "R") )
用户 系统 流逝 
0.02 0.00 0.02
> system.time( y3 <- fromJSON(json_str) )
用户 系统 流逝 
   0    0    0  

我们可以看到,C库比R库会快,因为数据量很小,所以0.02并不明显。当JSON串很大的时候,这个差距就会变得相当的大了。

另外,fromJSON默认使用的C库的方法,所以我们平时处理不用加method='C'的参数。

toJSON的性能测试


> system.time( y <- toJSON(json_data,method="C") )
用户 系统 流逝 
   0    0    0 
> system.time( y2 <- toJSON(json_data,method = "R") )
用户 系统 流逝 
0.02 0.00 0.01 
> system.time( y3 <- toJSON(json_data) )
用户 系统 流逝 
   0    0    0 

同上的解释。

2. RJSONIO包介绍

RJSONIO包,提供了2个主要的操作,把JSON串反序列化成R对象,把R对象序列化成JSON串,两个主要的函数fromJSON(), toJSON(),还包括几个辅助函数asJSVars(), basicJSONHandler(), Bob(), isValidJSON(), readJSONStream()。

RJSONIO包开发,是解决了rjson包序列化大对象慢的问题。RJSONIO依赖于底层的C语言类库libjson。

1). 安装和加载RJSONIO


install.packages("RJSONIO")
library(RJSONIO)

2). fromJSON() 从JSON到R
同rjson一样,测试fromJSON函数


> json_data <- fromJSON(paste(readLines("fin0.json"), collapse=""))
> json_data
$table1
$table1$time
[1] "130911"

$table1$data
$table1$data$code
[1] "TF1312" "TF1403" "TF1406"

$table1$data$rt_time
[1] 130911 130911 130911

$table2
$table2$time
[1] "130911"

$table2$data
$table2$data$contract
[1] "TF1312" "TF1312" "TF1403"

$table2$data$jtid
[1] 99 65 21

我们发现与 rjson的解析结果是一样,都是基于list的

取叶子节点:


> json_data$table1$data$code
[1] "TF1312" "TF1403" "TF1406"

> json_data$table1$data$code[1]
[1] "TF1312"

3). toJSON() 从R到JSON
toJSON测试


> json_str<-toJSON(json_data)

> print(json_str)
[1] "{\n \"table1\": {\n \"time\": \"130911\",\n\"data\": {\n \"code\": [ \"TF1312\", \"TF1403\", \"TF1406\" ],\n\"rt_time\": [ 1.3091e+05, 1.3091e+05, 1.3091e+05 ] \n} \n},\n\"table2\": {\n \"time\": \"130911\",\n\"data\": {\n \"contract\": [ \"TF1312\", \"TF1312\", \"TF1403\" ],\n\"jtid\": [     99,     65,     21 ] \n} \n} \n}"

> cat(json_str)
{
 "table1": {
 "time": "130911",
"data": {
 "code": [ "TF1312", "TF1403", "TF1406" ],
"rt_time": [ 1.3091e+05, 1.3091e+05, 1.3091e+05 ] 
} 
},
"table2": {
 "time": "130911",
"data": {
 "contract": [ "TF1312", "TF1312", "TF1403" ],
"jtid": [     99,     65,     21 ] 
} 
} 
}

toJSON函数输出与rjson是不一样的,这个输出是格式化的。

输出到文件:


> writeLines(json_str, "fin0_io.json")

文件结果:


{
 "table1": {
 "time": "130911",
"data": {
 "code": [ "TF1312", "TF1403", "TF1406" ],
"rt_time": [ 1.3091e+05, 1.3091e+05, 1.3091e+05 ] 
} 
},
"table2": {
 "time": "130911",
"data": {
 "contract": [ "TF1312", "TF1312", "TF1403" ],
"jtid": [     99,     65,     21 ] 
} 
} 
}

4). isValidJSON() 验证JSON是否合法
验证JSON的格式,是否合法。


> isValidJSON(json_str)
Error in file(con, "r") : cannot open the connection

> isValidJSON(json_str,TRUE)
[1] TRUE

> isValidJSON(I('{"foo" : "bar"}'))
[1] TRUE
> isValidJSON(I('{foo : "bar"}'))
[1] FALSE

4). asJSVars() 转换为Javascript变量格式


> cat(asJSVars( a = 1:10, myMatrix = matrix(1:15, 3, 5)))
a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] ;

myMatrix = [ [ 1, 4, 7, 10, 13 ],
           [ 2, 5, 8, 11, 14 ],
           [ 3, 6, 9, 12, 15 ] ] ;

得到JS两个变量,数组a和二维数组myMatrix.

3. 自定义JSON的实现

我们把R的data.frame对象转成我们定义的JSON格式。

下面JSON的定义格式


[
    {
        "code": "TF1312",
        "rt_time": "152929",
        "rt_latest": 93.76,
        "rt_bid1": 93.76,
        "rt_ask1": 90.76,
        "rt_bsize1": 2,
        "rt_asize1": 100,
        "optionValue": -0.4,
        "diffValue": 0.6
    }
]

R语言data.frame对象


df<-data.frame(
  code=c('TF1312','TF1310','TF1313'),
  rt_time=c("152929","152929","152929"),
  rt_latest=c(93.76,93.76,93.76),
  rt_bid1=c(93.76,93.76,93.76),
  rt_ask1=c(90.76,90.76,90.76),
  rt_bsize1=c(2,3,1),
  rt_asize1=c(100,1,11),
  optionValue=c(-0.4,0.2,-0.1),
  diffValue=c(0.6,0.6,0.5)
)

> df
    code rt_time rt_latest rt_bid1 rt_ask1 rt_bsize1 rt_asize1 optionValue diffValue
1 TF1312  152929     93.76   93.76   90.76         2       100        -0.4       0.6
2 TF1310  152929     93.76   93.76   90.76         3         1         0.2       0.6
3 TF1313  152929     93.76   93.76   90.76         1        11        -0.1       0.5

直接使用toJSON,输出的JSON串是按列组合成了数组,并不是我们想要的。


> cat(toJSON(df))
{
 "code": [ "TF1312", "TF1310", "TF1313" ],
"rt_time": [ "152929", "152929", "152929" ],
"rt_latest": [  93.76,  93.76,  93.76 ],
"rt_bid1": [  93.76,  93.76,  93.76 ],
"rt_ask1": [  90.76,  90.76,  90.76 ],
"rt_bsize1": [      2,      3,      1 ],
"rt_asize1": [    100,      1,     11 ],
"optionValue": [   -0.4,    0.2,   -0.1 ],
"diffValue": [    0.6,    0.6,    0.5 ] 
}

对data.frame进行数据处理


library(plyr)
> cat(toJSON(unname(alply(df, 1, identity))))
[
 {
 "code": "TF1312",
"rt_time": "152929",
"rt_latest":  93.76,
"rt_bid1":  93.76,
"rt_ask1":  90.76,
"rt_bsize1":      2,
"rt_asize1":    100,
"optionValue":   -0.4,
"diffValue":    0.6 
},
{
 "code": "TF1310",
"rt_time": "152929",
"rt_latest":  93.76,
"rt_bid1":  93.76,
"rt_ask1":  90.76,
"rt_bsize1":      3,
"rt_asize1":      1,
"optionValue":    0.2,
"diffValue":    0.6 
},
{
 "code": "TF1313",
"rt_time": "152929",
"rt_latest":  93.76,
"rt_bid1":  93.76,
"rt_ask1":  90.76,
"rt_bsize1":      1,
"rt_asize1":     11,
"optionValue":   -0.1,
"diffValue":    0.5 
} 
]

这回就对了,正是我希望的按行输出的结果!!通过alply函数做了变换。

4. JSON性能比较

性能比较我们做2组测试:

  • 1. rjson和RJSONIO 对大对象进行序列化(toJSON)测试
  • 2. RJSONIO包,序列化(toJSON) 列式输出和行式输出的测试

1). rjson和RJSONIO 对大对象进行序列化(toJSON)测试
创建一个rjson测试脚本,在命令行启动。


> library(rjson)
>
> df<-data.frame(
+   a=rep(letters,10000),
+   b=rnorm(260000),
+   c=as.factor(Sys.Date()-rep(1:260000))
+ )
>
> system.time(rjson::toJSON(df))
1.01 0.02 1.03
> system.time(rjson::toJSON(df))
1.01 0.03 1.04
> system.time(rjson::toJSON(df))
0.98 0.05 1.03

同样,再创建一个RJSONIO测试脚本,在命令行启动。


> library(RJSONIO)
>
> df<-data.frame(
+   a=rep(letters,10000),
+   b=rnorm(260000),
+   c=as.factor(Sys.Date()-rep(1:260000))
+ )
>
> system.time(RJSONIO::toJSON(df))
2.23 0.02 2.24
> system.time(RJSONIO::toJSON(df))
2.30 0.00 2.29
> system.time(RJSONIO::toJSON(df))
2.25 0.01 2.26

对比结果发现,rjson的性能优于RJSONIO。

2). rjson和RJSONIO,序列化(toJSON) 列式输出和行式输出的测试

创建一个rjson测试脚本,在命令行启动。


> library(rjson)
> library(plyr)
>
> df<-data.frame(
+   a=rep(letters,100),
+   b=rnorm(2600),
+   c=as.factor(Sys.Date()-rep(1:2600))
+ )
>
> system.time(rjson::toJSON(df))
0.01 0.00 0.02
> system.time(rjson::toJSON(df))
0.01 0.00 0.02

> system.time(rjson::toJSON(unname(alply(df, 1, identity))))
1.55 0.02 1.56
> system.time(rjson::toJSON(unname(alply(df, 1, identity))))
0.83 0.00 0.83

同样,再创建一个RJSONIO测试脚本,在命令行启动。


> library(RJSONIO)
> library(plyr)
>
> df<-data.frame(
+   a=rep(letters,100),
+   b=rnorm(2600),
+   c=as.factor(Sys.Date()-rep(1:2600))
+ )
>
> system.time(RJSONIO::toJSON(df))
0.03 0.00 0.03
> system.time(RJSONIO::toJSON(df))
0.04 0.00 0.03

> system.time(RJSONIO::toJSON(unname(alply(df, 1, identity))))
2.82 0.02 2.84
> system.time(RJSONIO::toJSON(unname(alply(df, 1, identity))))
2.06 0.00 2.06

通过测试我们发现,用toJSON直接列式输出,比行式输出要高效的多。同时,rjson要比RJSONIO高效。

转载请注明出处:
http://blog.fens.me/r-json-rjson/

打赏作者

Mongoose使用案例–让JSON数据直接入库MongoDB

为什么用Nodejs?为什么用MongoDB?从领域语言和代码简洁之道来看,这是我非常关心的问题。

Nodejs基于Javascript,MongoDB脚步同样也是基于Javascript。而且他们的数据存储格式都是JSON,这就是为什么要把他们放在一起的原因了。如果程序前后端能直接处理JSON,我想数据处理过程又可以极大的减化了,代码量又将低少1/5。多么的兴奋啊!让我们来动手验证一下想法吧。

关于作者:

  • 张丹(Conan), 程序员Java,R,PHP,Javacript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-mongoose-json/

程序代码已经上传到github有需要的同学,自行下载。
https://github.com/bsspirit/nodejs-demo

本文重点介绍web前端通过JQuery发起POST提交JSON数据,通过Mongoose直接插入或更新到MongoDB。

工程目录沿用nodejs-demo,增加/mongoose路径及对应文件。

关于nodejs-demo项目介绍,请参考文章:

Nodejs开发框架Express3.0开发手记–从零开始
http://blog.fens.me/nodejs-express3/

nodejs2

从零开始nodejs系列文章

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

 

文章目录

  1. 配置Mongoose
  2. 创建目录及文件
  3. 插入数据,POST提交JSON增加一条记录
  4. 查询数据,取出刚增加的记录

1. 配置Mongoose

增加mongoose的类库


cd d:/workspace/project/nodejs-demo
npm install mongoose

D:\workspace\project\nodejs-demo\node_modules\mongoose\node_modules\mongodb\node_modu
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.Cpp.InvalidPlatform.Targe
e_modules\mongodb\node_modules\bson\build\bson.vcxproj]
mongoose@3.6.10 node_modules\mongoose
├── muri@0.3.1
├── hooks@0.2.1
├── sliced@0.0.3
├── mpath@0.1.1
├── ms@0.1.0
├── mpromise@0.2.1 (sliced@0.0.4)
└── mongodb@1.3.3 (kerberos@0.0.2, bson@0.1.8)

安装时,有64位兼容性错误提示没关系,Mongoose类库安装完成。

增加models目录
mkdir models

在models目录,增加mongodb.js文件


var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/nodejs');
exports.mongoose = mongoose;

指定Mongo的数据库名为nodejs

2.创建目录及文件

在models目录,增加数据模型Movie.js


var mongodb = require('./mongodb');
var Schema = mongodb.mongoose.Schema;
var MovieSchema = new Schema({
name : String,
alias : [String],
publish : Date,
create_date : { type: Date, default: Date.now},
images :{
coverSmall:String,
coverBig:String,
},
source :[{
source:String,
link:String,
swfLink:String,
quality:String,
version:String,
lang:String,
subtitle:String,
create_date : { type: Date, default: Date.now }
}]
});
var Movie = mongodb.mongoose.model("Movie", MovieSchema);
var MovieDAO = function(){};
module.exports = new MovieDAO();

指定Mongo的数据库集为Movie

数据类型,包括了String,Date,Array,Mixed]

打开app.js增加访问路径


var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, movie = require('./routes/movie')
, http = require('http')
, path = require('path')
, ejs = require('ejs')
, SessionStore = require("session-mongoose")(express);
...
app.get('/movie/add',movie.movieAdd);//增加
app.post('/movie/add',movie.doMovieAdd);//提交
app.get('/movie/:name',movie.movieAdd);//编辑查询
app.get('/movie/json/:name',movie.movieJSON);//JSON数据

在routes目录,增加movie.js


var Movie = require('./../models/Movie.js');
exports.movieAdd = function(req, res) {
if(req.params.name){//update
return res.render('movie', {
title:req.params.name+'|电影|管理|moive.me',
label:'编辑电影:'+req.params.name,
movie:req.params.name
});
} else {
return res.render('movie',{
title:'新增加|电影|管理|moive.me',
label:'新增加电影',
movie:false
});
}
};
exports.doMovieAdd = function(req, res) {
res.send({'success':true});
};

在views目录,增加movie.html


<% include header.html %>
<div class="container-fluid">
<div class="row-fluid">
<div class="span8">
<form>
<fieldset>
<legend><%=label%></legend>
<textarea id="c_editor" name="c_editor" class="span12" rows="10"></textarea>
<button id="c_save" type="button" class="btn btn-primary">保存</button>
</fieldset>
<form>
</div>
</div>
</div>
<% include footer.html %>

网页效果:http://localhost:3000/movie/add

movieInsert

3. 插入数据,POST提交JSON增加一条记录

基础环境,都搭建好后,我们开台准备向mongodb中插入数据。

首先创建一个json数据文件,这样我们可以方便点,直接读入这个文件,创建JSON数据对象了。

在public/javascripts/目录,增加movie.json文件


{
"name": "未来警察",
"alias": ["Future X-Cops ","Mei loi ging chaat"],
"publish": "2010-04-29",
"images":{
"coverBig":"/img/movie/1_big.jpg",
"coverSmall":"/img/movie/1_small.jpg"
},
"source":[{
"source":"优酷",
"link":"http://www.youku.com",
"swfLink":"http://player.youku.com/player.php/sid/XMTY4NzM5ODc2/v.swf",
"quality":"高清",
"version":"正片",
"lang":"汉语",
"subtitle":"中文字幕"
},{
"source":"搜狐",
"link":"http://tv.sohu.com",
"swfLink":"http://share.vrs.sohu.com/75837/v.swf&topBar=1&autoplay=false&plid=3860&pub_catecode=",
"quality":"高清",
"version":"正片",
"lang":"汉语",
"subtitle":"中文字幕"
}]
}

在public/javascripts/目录,增加jquery.json-2.4.js类库


<script src="/javascripts/jquery-1.9.1.min.js"></script>
<script src="/javascripts/bootstrap.min.js"></script>
<script src="/javascripts/jquery.json-2.4.js"></script>
<script src="/javascripts/movie.js"></script>
</body>
</html>

在public/javascripts/目录,增加movie.js文件,作为前端脚本


$(function() {
var mdata={};
var url = '/javascripts/movie.json';
$.getJSON(url, function(data) {
mdata=data;
render_editor_form(mdata);
render_event_form(mdata);
});
var render_editor_form=function(data){
$('#c_editor').val($.toJSON(data));
};
var render_event_form=function(){
$('#c_save').on('click',function(event){
var data = {};
data['content'] = mdata;
$.ajax({
type: "POST",
url: '/movie/add',
data: data,
success: function (data, textStatus){
if(data.success){
$('#msg').html('成功保存!');
$('#msg').addClass('alert alert-success');
$(location).attr('href','/movie/'+mdata.name);
} else {
$('#msg').html(data.err);
$('#msg').addClass('alert alert-error');
}
}
});
});
};
});

修改views/footer.html,增加movie.js文件引用,同时增加jquery.json包


<script src="/javascripts/jquery-1.9.1.min.js"></script>
<script src="/javascripts/bootstrap.min.js"></script>
<script src="/javascripts/jquery.json-2.4.js"></script>
<script src="/javascripts/movie.js"></script>
</body>
</html>

网页效果:http://localhost:3000/movie/add

movieInsert2

在models/Movie.js,增加save方法


MovieDAO.prototype.save = function(obj, callback) {
var instance = new Movie(obj);
instance.save(function(err){
callback(err);
});
};

在routes/movie.js,调用save方法


exports.doMovieAdd = function(req, res) {
console.log(req.body.content);
var json = req.body.content;
if(json._id){//update
} else {//insert
Movie.save(json, function(err){
if(err) {
res.send({'success':false,'err':err});
} else {
res.send({'success':true});
}
});
}
};

控制台日志


Express server listening on port 3000
{ name: '未来警察',
alias: [ 'Future X-Cops ', 'Mei loi ging chaat' ],
publish: '2010-04-29',
images:
{ coverBig: '/img/movie/1_big.jpg',
coverSmall: '/img/movie/1_small.jpg' },
source:
[ { source: '优酷',
link: 'http://www.youku.com',
swfLink: 'http://player.youku.com/player.php/sid/XMTY4NzM5ODc2/v.swf',
quality: '高清',
version: '正片',
lang: '汉语',
subtitle: '中文字幕' },
{ source: '搜狐',
link: 'http://tv.sohu.com',
swfLink: 'http://share.vrs.sohu.com/75837/v.swf&topBar=1&autoplay=false&plid=3860&pub_ca
quality: '高清',
version: '正片',
lang: '汉语',
subtitle: '中文字幕' } ] }
POST /movie/add 200 57ms - 21b

数据已插入MongoDB

movieInsert3

4. 查询数据,取出刚增加的记录

models/Movie.js,增加findByName方法


MovieDAO.prototype.findByName = function(name, callback) {
Movie.findOne({name:name}, function(err, obj){
callback(err, obj);
});
};

routes/movies.js,增加movieJSON


exports.movieJSON = function(req, res) {
Movie.findByName(req.params.name,function(err, obj){
res.send(obj);
});
}

前端javascripts/movie.js,从/movie/json/xxx处取数据


var mdata={};
var url = '/javascripts/movie.json';
var movie=$('#c_editor').attr('movie')
if(movie){
url = '/movie/json/'+movie;
}

修改 views/movie.html


<textarea id="c_editor" name="c_editor" rows="10" <%= (movie?'"movie='+movie+'"':'') %>></textarea>

访问我们的网页

http://localhost:3000/movie/未来警察

movieInsert4

数据从/movie/json/未来警察,处读取。完成尝试。

修改操作与插入的操作类似,我就不做演示了。

希望此文对大家有所帮助。

以上程序代码,我已经上传到github有需要的同学,自行下载。
https://github.com/bsspirit/nodejs-demo

转载请注明出处:
http://blog.fens.me/nodejs-mongoose-json/

打赏作者