深入理解JSON

文章声明:本文是在某APP上看到的一位程序员发表的文章所做的笔记,无法帖地址,在此说明。
JSON我们都不陌生,但是你真正的了解过JSON么?它是什么?为什么说它是轻量级的数据交换格式以及它的工作方式是什么样的?别着急,下面我们一步一步的深入了解JSON这个东西。

var student = {
              firstName : 'emo',
              lastName : 'guoshi',
              age : 23,
              'sex' : "男"
         };

上面这个JSON字符串能看出来有哪里不准确么?它被序列化成字符串是什么样的?别着急在控制台输出,我们先追根溯源,从它的组成来下手。

一、JSON是一种基于文本的轻量级的数据交换格式
首先从标题能提取出三个关键词:基于文本、轻量级、数据交换格式。
先来分析一下什么是数据交换格式,举例,你想将一个学生的信息传给别人,你会想到用什么方式?

  1. 姓名“某某某”,学号“123”,年龄“23;
  2. <student>
    <name>某某某</name>
    <sno>123</sno>
    <age>23</age>
    </student>

    3.` {
    “name”: “某某某”,
    “sno”: “guoshi”,
    “age “: “23”
    }

以上列出的未列出的都是数据传输的方法,传输的目的是一样的,只不过是传输格式的组成有区别,JSON也只是其中的一种格式而已,下面来看一下JSON的官网介绍。
JSON官网链接:去看看

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.

JSON is built on two structures:

1.***A collection of name/value pairs***. In various languages, this is realized as an object, record, struct, dictionary, hash table, keyed list, or associative array.

2.An ordered list of values. In most languages, this is realized as an array, vector, list, or sequence.

These are universal data structures. Virtually all modern programming languages support them in one form or another. It makes sense that a data format that is interchangeable with programming languages also be based on these structures.
这是官网上的原话,介绍了JSON的组成,看我加粗的斜体字,明确说明了JSON是轻量级的基于文本的数据传输格式。至于说JSON是JavaScript的子集,这个问题等一下再说。
1、基于文本
回想曾经写过的JSON,我们都是将内容用引号(下面说JSON的格式会说到)包裹然后以键值对的形式发送出去,也就是我们常说的JSON字符串,就是因为JSON 是基于文本格式的。
2、轻量级
现在比较流行的数据传输方式有XML和JSON。XML传输数据需要以标签对包裹,这样就会占据很多空间,也就更耗费带宽。通过上面的栗子可以看到,相同内容下JSON更轻量,占据的带宽会更小。可以想象,如果是大数据量的请求传递情况下JSON是更好的选择。
3、使用友好
JSON 还有一个优势,就是易读写(针对人),易解析(针对机器)。
举个栗子:
ajax向后台请求数据,后台解析后返回相应数据,简略流程如下:
1.前台构造一个JSON对象(有待探究),用于包装要传递的数据,然后将JS 对象转化为JSON字符串,再发送请求到后端。
2.后端PHP接收到这个字符串后,将JSON字符串转化为PHP对象,然后处理请求。
可以看到,数据在传递的过程中有三种不同的载体,一种是JSON对象,一种是JSON字符串,一种是PHP对象。其中JS对象和PHP对象是两个语言的东西,本是不能互通的,但是因为都是使用的JSON格式来传递和处理数据,都能把JSON这种数据格式很容易的转化为自己能理解的数据结构,这就使得交互成为可能。
综上所述,因为JSON轻量、基于文本(独立于语言)、人机友好,就使得JSON的应用越来越广泛。
二、JSON是JS对象的子集
与其说JSON是JS的子集不如说JSON是ECMAScript的子集,关于ECMAScript的内容可以看这里:专门针对JSON的内容
上面定义了JSON的格式要求:

现在回头看一开始的那段代码,简直是错误百出,键名加双引号、属性值不能是单引号,按理说这样不符合JSON格式要求的对象是不能被JSON识别并序列化的,可我们在控制台却看到了它的数据结果,这就是我们JSON函数的功劳了,下面慢慢说。
JSON只是我们数据交互的一种工具, 所以我们就不要再纠结于JSON的父类是谁了,只要理解它,用好它就ok啦。
三、JSON对象的原生函数
我们经常会用到的json函数有两个:JSON.stringify(data)和JSON.parse(data)。
1.序列化成json字符串的函数:JSON.stringify(data)
它的函数签名是这样的:

    JSON.stringify(value[,replacer[,space]])

可以看到,这个方法可以传三个参数,不过我们经常传一个参数就满足要求了,比如说这样:

     JSON.stringify(student);

接下来我们来说说另外两个参数。
1.1第二个参数可以是函数,也可以是一个数组

  • 如果是一个函数,那么序列化过程中的每个属性都会被这个函数转化和处理;
  • 如果是一个数组,那么只有包含在这个数组中的属性才会被序列化到最终的JSON字符串之中;
  • 如果是null,和空没有区别,除非你想使用第三个参数

上代码:

//第二个参数是函数的情况
         var student1 = JSON.stringify(student,function(key,val){
          if(key === "firstName"){
            return student[key] = "emoj";
          }else if(key === "age"){
            return val + 10;
          }else{
            return val; 
            //如果将else去掉 结果会返回undefined,这是因为必须要保证每个属性都要有返回
          }
         });

         console.log(student1);
         //结果  {"firstName":"emoj","lastName":"guoshi","age":33,"sex":"男"}


         //第二个参数是数组的情况
         var student2 = JSON.stringify(student,['firstName','age','phone']);

         console.log(student2);
         //结果  {"firstName":"emoj","age":23}

第二个参数是函数的情况比较直观,接受两个参数key和value,分别对应JSON对象的键和值,需要注意的是这个函数需要对每一个属性也就是键有返回值,否则我们得到的结果就是undefined。
如果第二个参数是数组的话,其实它是取JSON对象的key和数组里的项取交集,像上面代码那样,只有”firstName”和”age”是存在于JSON对象的key里的,”phone”这个数组项就被忽略了,并且没有出现在这个数组里的”lastName”和”sex”也被忽略了。
1.2第三个参数
制定缩进用的空白字符,取值有:

  • 1-10某个数字,代表几个空白字符
  • 字符串,用该字符串代替空格,最多取这个字符串的前10个字符

第三个参数仅用于美化输出,没什么实际应用,并且如果使用了还可能会造成格式的错误,不建议使用。

(重要)我们开头的那段“伪”JSON之所以能够正确的输出都是上面这个函数的功劳,也就是我们下面要说的“聪明”的地方,尽量都试一试。

  • 键名是单引号或者没有引号的,会自动加上双引号;
  • 最后一个属性后面有逗号的,会自动去掉;
  • 非数组对象的属性不能保证以特定的顺序出现在序列化之后的字符串中,也就是非数组对象不保证属性顺序和原来一致(如无特殊,不必在意)
  • 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。栗子:`var student = {
    “name” : new String(“lilf”),
    “age” : Number(22)
    };

    console.log(JSON.stringify(student));
    //结果 {“name”:”lilf”,”age”:22}`

  • undefined、任意函数(有一个函数后面会说到)以及symbol值出现在非数组对象的属性值中,在序列化的过程中会被自动忽略,出现在数组中,会被转换成null;`var student = {
    “name” : undefined,
    “age” : function a(){}
    };

    console.log(JSON.stringify(student));
    //结果 {}

    var student = [
    undefined,
    function a(){},
    Symbol(“”)
    ];

    console.log(JSON.stringify(student));
    //结果 [null,null,null]`

  • NaN、infinity和-infinity,不论在数组中还是在非数组中,都会被转换成null;

  • 所有以symbol为键的属性都会被忽略,即使replacer参数中强制指定包含了它们;
  • 不可枚举的属性会被忽略。

2.JSON.parse——将JSON字符串解析为JSON数据结构
该函数的函数签名:

JSON.parse(text[,reviver])

如果第一个参数也就是传入的JSON字符串不是合法字符串的话,函数就会抛出错误,栗子如下:

JSON.parse({"name":lilf,"age":22});

这里写图片描述
所以,以后我们在使用JSON传递数据的时候最好使用本语言支持的JSON函数去操作,尽量避免手动拼接,以免出现错误难以Debug。
该函数还可以接收第二个参数,该参数必须是一个函数,这个函数的作用就是在属性已经被解析但是还没有返回之前对其进行处理再返回。

var student = {
    "name" : "emoj",
    "age" : 22,
    "hobby" : [{"hobbyname" : "basketball"},{"hobbyname" : "tennis"}],
    "work" : {"workname" : ["up","down"]}
   };

   var studentA = JSON.stringify(student);
   var i = 0;
   JSON.parse(studentA,function(key,val){

       console.log("key : ", key);
       console.log("value : ",val);
       console.log("=============");
   });

   /*
     key :  name
     value :  emoj
     =============
     key :  age
     value :  22
     =============
     key :  hobbyname
     value :  basketball
     =============
     key :  0
     value :  Object {}
     =============
     key :  hobbyname
     value :  tennis
     =============
     key :  1
     value :  Object {}
     =============
     key :  hobby
     value :  []
     =============
     key :  0
     value :  up
     =============
     key :  1
     value :  down
     =============
     key :  workname
     value :  []
     =============
     key :  work
     value :  Object {}
     =============
     key :  
     value :  Object {}
     =============
   */

仔细看一下上面的输出,我们可以发现这个遍历是由内而外的,为什么说是由内而外呢,我们可以看到在遍历属性完为“age”这一项时,理论上应该遍历“hobby”这一项,但结果却是遍历了“hobby”属性的值,。显然,这个由内而外是针对复合属性来说的,本质上,就是一个深度优先的遍历。
有两点需要注意:

  • 如果reviver返回undefined,则当前属性会从所属对象中删除,如果返回了其他值,则返回结果会成为当前属性新的属性值;

3影响JSON.stringify的神奇函数——object.toJSON
如果你在一个JS对象上实现了toJSON方法,那么调用JSON.stringify去序列化这个JS对象时,会将这个对象的toJSON方法的返回值作为参数去序列化。
栗子如下:

var info = {
      "msg" : "I LOVE YOU",
      "toJSON" : function(){
        var o = new Object();
        o.msg = "Go Die";
        return o;
      }
    };

    console.log(JSON.stringify(info));
    //结果  {"msg":"Go Die"}

Date类型的实例可以直接传给JSON.stringify做参数,就是因为其内置了toJSON方法。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值