在做Django项目时,用到了很多AJax的知识,说到Ajax就会涉及到json的知识,因此索性准备来一篇博客,将项目过程中遇到的问题记录下,以方便日后的查阅。
一.什么是JSon?
JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。 它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
说到这里大家可能还是难以理解,说白了,json就是一种通用的数据交换格式,有了json,我们就可以在不同语言之间交互数据,能够非常方便我们的操作,我们来看这样一个需求:Python负责爬取数据,Java用来做大数据分析:
例如:Python负责爬取数据,这些数据构成了一个大的字典,现在需要给Java处理!如果直接发送给Java处理,Java肯定处理不了,那是因为你Python中的字典,我Java压根不认识,因为你们的语法不一样,所以我处理不了!但是现在我就要处理这些数据,现在就需要一套标准,这套标准就是用来定义Python和Java之间的数据交换协议——例如这里的Json,我将Python的字典序列化成json格式的字符串,然后你Java拿到后再使用json进行反序列化成你认识的对象,这样我们就完成了不同语言之间的数据交换
再例如,我们使用Python做Web开发,经常需要和前端JS交互数据,此时又会用到json,例如将Python生成的字典序列化成如下json的字符串传递给前端,前端在使用json反序列化成JS能够识别的字符串即可。
大家要记住:
无论什么语言,序列化成的JSON格式都一样的,都是JSON字符串!但是反序列化后,分情况:使用js来反序列化json字符串得到的就是js支持的基本数据类型,使用java反序列化json字符串得到的就是java支持的基本数据类型,使用python反序列化也是同样的道理。
另外大家还要注意一点:JSON来源于JS中的数据类型,它只是取出了双引号的object类型(类似于字典),事实上json对象可以从JS对象中拿出一些标准,也可以从Java中拿,不论怎么拿,这套标准都是一样的。明白了上面所说的这些,我们来看下面这张图就清楚明了多了:
从上图中可以看到,Python中的基本数据类型和json中的基本数据类型存在一系列对应关系,例如Python中的字典对象对应到json中object类型,也就是说但凡是支持json数据格式的语言,其基本数据类型都和json有对应关系,有了这层对应关系,就可以直接使用json进行序列化,这也就是为什么我们在之前的Django项目中使用可以使用JSonresponse直接序列化一个字典对象的原因,并且前端可以不用反序列化就可以使用的原因,因为Python中的字典和json中object对象类型对应,所以不需要使用json.dumps先序列化成字符串,然后再返回给前端。
笔者曾经遇到过一个大坑:日期类型的序列化
我们每次对文章评论完毕后,都会显示评论时间,这个评论时间就是最终的入库时间,既然要在前端进行展示,我们需要将时间取出来然后传递回去,但问题在于我们在定义数据模型的时候,时间是一个DateTimeField的字段类型,JSon是无法序列化一个datetime.datetime的日期对象的,因为没有一个对应的关系,也就是说我们不能直接使用JsonResponse来进行序列化,要不然会报错:
前面我们说过,既然无法直接序列化,我就使用python中的json.dumps来先将你序列化成字符串,然后再传递给前端即可,这也就是我们为什么在comment视图中使用HttpResponse来传递后端数据,而不使用JsonResponse来序列化数据的原因,来看代码即可:
def comment(request): """ 服务端需要客户端传递过来三个数据: 1.评论内容,2.评论的文章,3.当前评论的根评论的ID:parent_comment_id 由于只有登入的用户可以评论,所以当前的用户ID就是登入用户的ID 如果当前评论存在根评论,那说明这次提交的是子评论,否则提交的就是根评论 """ comment_content = request.POST.get("content") article_id = request.POST.get("article_id") parent_comment_id = request.POST.get("parent_comment_id") user_id = request.user.nid """ 注意最终渲染到页面上的时间,是该条评论的入库时间,而不是该条评论的提交时间; """ comment_response = {} if parent_comment_id: # 提交的是子评论,构建评论树 """ 如果提交的是子评论,那么同样需要往comment表中插入一条记录,同时要 往文章表中的评论数+1,这里又是一个事务操作,这里使用create方法往评论表中插入一条数据的时候会得到一个 评论对象current_comment """ with transaction.atomic(): current_comment = Comment.objects.create(content=comment_content, user_id=user_id, article_id=article_id, parent_comment_id=parent_comment_id) Article.objects.filter(article_id=article_id).update(comment_count=F("comment_count") + 1) comment_response["parent_comment_content"] = current_comment.parent_comment.content comment_response["parent_comment_user"] = current_comment.parent_comment.user.username else: # 提交的就是根评论 """ 将根评论内容添加至comment表中 然后将文章表中的评论数comment_count自加1 """ with transaction.atomic(): current_comment = Comment.objects.create(content=comment_content, user_id=user_id, article_id=article_id) Article.objects.filter(article_id=article_id).update(comment_count=F("comment_count") + 1) # 给前端ajax返回相应的数据进行渲染 """ 这里有2点需要注意: 1.如果直接通过current_comment.comment_date获取当前记录的入库时间,此时该时间是一个datetime对象 如果直接使用进行序列化会报错。 """ comment_response["content"] = current_comment.content comment_response["comment_date"] = current_comment.comment_date.strftime("%Y-%m-%d %H:%m") comment_response["comment_id"] = current_comment.nid return HttpResponse(json.dumps(comment_response))
在上面的思路中,我们自己处理,借助于datetime模块的strfttime完成对日期对象的处理,然后使用json.dumps来序列化最终的字符串对象,如下所示:
comment_response["comment_date"] = current_comment.comment_date.strftime("%Y-%m-%d %H:%m") print(type(comment_response["comment_date"])) 此时它是一个普通的Python字符串对象
笔者继续给大家举一个实际例子:我们之前使用Ajax将编辑后的老师信息发送给后端数据库进行修改,看如下的代码:
$(document).ready(function () { //给table表里面的所有modal编辑按钮绑定事件,使用Jquery的事件委派 $("table").on("click", ".m-edit", function () { //一旦点击modal编辑讲师按钮,即可弹出模态框,myEditModal为模态框的唯一ID $("#myEditModal").modal("show"); //此时模态框中已经有值存在了,这是通过Ajax发出的get请求获取的值,要不然我们是无法重新编辑值的,接下来我们取值 var $tds = $(this).parent().parent().children(); // 获取表格中所有的td标签 var teacherID = $($tds[0]).text(); var teacherName = $($tds[1]).text(); var teacherGender = $($tds[2]).text(); var teacherSalary = $($tds[3]).text(); //使用Ajax获取老师任教的班级,因为teacherID和teacherName在请求teacher_list时,就可以获取到 //但是老师的班级是个列表怎么获取,我们这里使用Ajax来获取 $.ajax({ url: "/modal_teacher_list/?teacher_id="+teacherID, type: "GET", success: function (data) { console.log(data); var classIDs = JSON.parse(data); // 获取当前老师所带班级的ID $("#editTeacherSelect").val(classIDs); } }); //然后来填充模态框中各个字段的值 $("#editTeacherName").val(teacherName); $("#editTeacherGender").val(teacherGender); $("#editTeacherSalary").val(teacherSalary); $("#editTeacherID").val(teacherID); }); // 给编辑老师信息模态框的提交按钮绑定事件 $("#edit-modal-submit").on("click", function () { //获取编辑之后的值 var teacherID = $("#editTeacherID").val(); var teacherName = $("#editTeacherName").val(); var teacherGender = $("#editTeacherGender").val(); var teacherSalary = $("#editTeacherSalary").val(); // 获取编辑后老师所带的班级ID,一个老师带多个班级,所以这里得到的是一个数组,思考后面怎么将数组传递到后台 var classIDs = $("#editTeacherSelect").val(); //使用Ajax提交到后端 $.ajax({ url: "/modal_edit_teacher/", type: "post", //注意使用Ajax提交数组到前端时,需要使用JSON.stringify()序列化为字符串 data: {"teacher_id": teacherID, "teacher_name":teacherName, "teacher_gender": teacherGender, "teacher_salary": teacherSalary, "class_ids": JSON.stringify(classIDs)}, success: function (data) { //从前端接接收到的为json字符串,需要反序列化为js对象 var dataObj = JSON.parse(data); if (dataObj.status === 0){ location.reload() }else{ //如果更新失败,那就隐藏模态框,同时弹出swall效果 $("#myEditModal").modal("hide"); swal("我擦", "更新失败了", "error"); } } }) }) });
在上面的需求中,一个老师可以带领多个班级,这里的班级在JS中是一个数组,我们不可能直接将数组放在Ajax中的data中进行传递,因此需要使用JS的序列化方法,将其序列为一个字符串对象,然后再进行传递,如下:
data: {"teacher_id": teacherID, "teacher_name":teacherName, "teacher_gender": teacherGender,"teacher_salary": teacherSalary, "class_ids": JSON.stringify(classIDs)}
后台在收到序列化后的班级列表字符串后,肯定需要使用Python来反序列化为Python中的列表对象:
teacher_class_ids = request.POST.get("class_ids") teacher_ids = json.loads(teacher_class_ids)
二.正确认识JSon格式
笔者在做项目的过程中经常将JSON对象,JSON字符串以及两者之间互转弄混淆,基于此,笔者在此整理一下,方便日后查阅:
【001】Json对象:是指符合json格式要求的js对象,前面笔者提到过json就是来源于JS;既然是对象,那就可以使用对象名.属性名来调用,看如下的例子:
var person={"name":"zhangsan","sex":"男","age":"24"}; //json对象 alert(person.name);//zhangsan alert(typeof person);//object
person就是json对象。可以用perosn.name这种方式进行属性的调用。第三行代码就是看person的类型,为object类型;注意json对象中的属性名必须要是双引号,例如下面这样的格式是不对的:
{ name: "张三", ‘age‘: 32 } // 属性名必须使用双引号 [32, 64, 128, 0xFFF] // 不能使用十六进制值 { "name": "张三", "age": undefined } // 不能使用undefined { "name": "张三", "birthday": new Date(‘Fri, 26 Aug 2011 07:13:10 GMT‘), "getName": function() {return this.name;} // 不能使用函数和日期对象 }
【002】字符串,我们常说的JavaScript中的字符串是单引号或者双引号引起来的,而Json字符串指的是符合json格式要求的js字符串,如下:
var person=‘{"name":"zhangsan","sex":"男","age":"24"}‘;//json字符串 alert(person);//{"name":"zhangsan","sex":"男","age":"24"} alert(typeof person);//string
person就是一个json字符串,之所以叫json字符串,因为字符串的格式符合json的格式,第三行代码也匹配其中的类型为string。
【003】JSON字符串和JOSN对象的转换
json字符串转json对象,调用parse方法:
var person=‘{"name":"zhangsan","sex":"男","age":"24"}‘;//json字符串 var personObject = JSON.parse(person); alert(personObject.name);//zhangsan
json对象转为json字符串,调用stringify方法:
var person={"name":"zhangsan","sex":"男","age":"24"};//json对象 var personString = JSON.stringify(person); alert(personString);
关于Django中Ajax的使用我们将在下节中详细说明,希望各位好好掌握本篇博客的知识!
【004】stringify与parse方法
JSON.parse(): 用于将一个 JSON 字符串转换为 JavaScript 对象 eg: console.log(JSON.parse(‘{"name":"Yuan"}‘)); console.log(JSON.parse(‘{name:"Yuan"}‘)) ; // 错误 console.log(JSON.parse(‘[12,undefined]‘)) ; // 错误 JSON.stringify(): 用于将 JavaScript 值转换为 JSON 字符串。 eg: console.log(JSON.stringify({‘name‘:"egon"})) ;
【005】 最后给各位推荐一个可以格式化JSon字符串的在线网址,非常方便:https://www.bejson.com/
参考网址:https://www.cnblogs.com/gyx19930120/p/4419971.html
http://blog.csdn.net/android_xue/article/details/69488793
原文地址:https://www.cnblogs.com/pyspark/p/8166737.html