热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JSON的正确用法:Python、MongoDB、JavaScript与AjaxJSON的正确用法:Python、MongoDB、JavaScript与Ajax...

本文主要总结网站编写以来在传递JSON数据方面遇到的一些问题以及目前采用的解决方案。网站数据库采用MongoDB,后端是Python,前端采用“半分离”

本文主要总结网站编写以来在传递 JSON 数据方面遇到的一些问题以及目前采用的解决方案。网站数据库采用 MongoDB,后端是 Python,前端采用“半分离”形式的 Riot.js,所谓半分离,是说第一页数据是通过服务器端的模板引擎直接渲染到 HTML 中,从而避免首页两次加载的问题,而其它动态内容则采用 Ajax 加载。整个流程中数据都是通过 JSON 格式传递的,但是在不同的环节中需要采用不同的方式并遇到一些不同的问题,本文主要做记录、总结。

json

1. What is JSON?

JSON(Javascript Object Notation) 是一种由道格拉斯·克罗克福特构想设计、轻量级的数据交换语言,它的前辈 XML 可能更早被人们所熟知。当然 JSON 并不是为了取代 XML 而存在的,只是相比于 XML 它更小巧、更适合在网页开发中用作数据传递(JSON 之于 Javascript 就像 XML 之于 Lisp)。从名字上可以看出,JSON 的格式符合 Javascript 语言中“对象”的语法格式,除了 Javascript 之外,很多其他语言中也具有类似的类型,例如 Python 中的字典(dict),除了编程语言之外,一些基于文档存储的 NoSQL 非关系型数据库也选择 JSON 作为其数据存储格式,例如 MongoDB。

总的来说,JSON 定义一种标记格式,可以非常方便地在编程语言中的变量数据与字符串文本数据之间相互转换。JSON 描述的数据结构包括以下这几种形式:

  1. 对象:{key: value}
  2. 列表:[obj, obj,...]
  3. 字符串:"string"
  4. 数字:数字
  5. 布尔值:true/false

了解了 JSON 的基本概念之后,下面分别针对上图中的几个数据交互环节进行总结。

2. Python <&#61;> MongoDB

Python 与 MongoDB 之间的交互主要由现有的驱动库提供支持&#xff0c;包括 PyMongo、Motor 等&#xff0c;而这些驱动所提供的接口都是非常友好的&#xff0c;我们不需要了解任何底层的实现&#xff0c;只要对 Python 原生的字典类型进行操作即可&#xff1a;

import motor
client &#61; motor.motor_tornado.MotorClient() db &#61; client[&#39;test&#39;] user_col &#61; db[&#39;user&#39;] user_col.insert(dict( name &#61; &#39;Yu&#39;, is_admin &#61; True, ))

唯一需要注意的是 MongoDB 中的索引项 _id 是通过 ObjectId("572df0b78a83851d5f24e2c1")存储的&#xff0c;而对应的 Python 对象为 bson.objectid.ObjectId&#xff0c;因此在查询时需要以此对象的实例进行&#xff1a;

from bson.objectid import ObjectId
user &#61; db.user.find_one(dict( _id &#61; ObjectId("572df0b78a83851d5f24e2c1") ))

3. Python <&#61;> Ajax

前端与后端之间的数据交流比较常用的是通过 Ajax 完成&#xff0c;这时遇到了第一个不大不小的坑。在之前的一篇文章中&#xff0c;我总结了一次 Python 编码的坑&#xff0c;我们知道 HTTP 传递过程中肯定不存在 JSON/XML &#xff0c;一切都是二进制数据&#xff0c;但是我们可以选择让前端用什么样的方式解读这些数据&#xff0c;即通过设定 Header 中的 Content-Type&#xff0c;一般传递 JSON 数据时将其设定为 Content-Type: application/json&#xff0c;在 Tornado 最新版本中&#xff0c;只需要直接写入字典类型即可&#xff1a;

# Handler
async def post(self): user &#61; await self.db.user.find_one({}) self.write(user)

于是迎来了第一个错误&#xff1a;TypeError: ObjectId(&#39;572df0b58a83851d5f24e2b1&#39;) is not JSON serializable。追溯原因&#xff0c;虽然 Tornado 帮我们简化了操作&#xff0c;但在像 HTTP 中写入字典类型时仍然需要经历一次 json.dumps(user) 操作&#xff0c;而对于 json.dumps 来说&#xff0c;ObjectId 类型是非法的。于是我选择了最直观的解决方案&#xff1a;

import json
from bson.objectid import ObjectId class JSONEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, ObjectId): return str(obj) return super().default(self, obj) # Handler async def post(self): user &#61; await self.db.user.find_one({}) self.write(JSONEncoder.encode(user))

这次不会再出错了&#xff0c;我们自己的 JSONEncoder 可以应对 ObjectId 了&#xff0c;但另一个问题也出现了&#xff1a;

html

JSONEncoder.encode 之后字典类型被转换成字符串&#xff0c;写入 HTTP 之后 Content-Type 变为text/html&#xff0c;这时前端将认为接收的数据为字符串而不是可用的 Javascript Object。当然还有进一步的弥补方案&#xff0c;那就是前端再进行一次转换&#xff1a;

$.post(API, {}, function(res){ data &#61; JSON.parse(res); console.log(data._id); })

问题暂时解决了&#xff0c;在整个过程中 JSON 的变换是这样的&#xff1a;

Python &#61;&#61;> json.dumps &#61;&#61;> HTTP &#61;&#61;> Javascript &#61;&#61;> JSON.parse
dict &#61;&#61;> str &#61;&#61;> binary &#61;&#61;> string &#61;&#61;> Object

结果第二个问题来了&#xff0c;当数据中存在一些特殊字符时&#xff0c;JSON.parse 将出现错误&#xff1a;

JSON.parse("{&#39;abs&#39;: &#39;\n&#39;}"); // VM536:1 Uncaught SyntaxError: Unexpected token &#39; in JSON at position 1(…)

这就是在遇到问题是只着眼解决眼前错误导致后续一连串改动所带来的弊病。我们沿着上面 JSON 变换的链条向上追溯&#xff0c;看有没有更好的解决方案。很简单&#xff0c;遵循传统规则&#xff0c;出现特例的时候&#xff0c;改变自身适应规则&#xff0c;而不是改变规则&#xff1a;

# Handler
async def post(self): user &#61; await self.db.user.find_one({}) user[&#39;_id&#39;] &#61; str(user[&#39;_id&#39;]) self.write(user)

当然&#xff0c;如果是多条数据的列表形式&#xff0c;还需要进一步改造&#xff1a;

# DB
async def get_top_users(self, n &#61; 20): users &#61; [] async for user in self.db.user.find({}).sort(&#39;rank&#39;, -1).limit(n): user[&#39;_id&#39;] &#61; str(user[&#39;_id&#39;]) users.append(user) return users

4. Python <&#61;> HTML&#43;Riot.js

如果上面的问题可以通过遵守规则来解决&#xff0c;那么接下来这个问题就是一个挑战规则的故事。除去 Ajax 动态加载部分&#xff0c;网页上的其他数据是通过后端模板引擎渲染得来的&#xff0c;也就是说是 Hard-coding 为 HTML 的。在浏览器加载并解析这个 HTML 文件之前它们只是纯文本文件&#xff0c;而我们需要的是直接将数据塞仅 >

这样写是对的&#xff0c;但是要解决上面提到的 ObjectId() 问题还是需要一些额外的处理&#xff08;尤其是引号问题&#xff09;。另外为了解决 ObjectId 的问题我还尝试了一种比较蠢的方法&#xff08;在上面的 JSON.parse 遇到错误之前&#xff09;&#xff1a;

# Handler
async def get(self): users &#61; self.db.get_top_users() render_data &#61; dict( users &#61; JSONEncoder.encode(users) ) self.render(&#39;users.html&#39;, **render_data)


>> >

其实跟第 3 小节的问题一样&#xff0c;模板引擎渲染过程与 HTTP 传输过程是类似的&#xff0c;不同的是在模板中字符串变量就是纯粹的值&#xff08;没有引号&#xff09;&#xff0c;因此完全可以用生成 Javascript 脚本文件的形式渲染变量而无需顾虑特殊字符&#xff08;下面的 {% raw ... %} 是 Tornado 模板用于防止特殊符号被 HTML 编码的语法&#xff09;&#xff1a;


>> >

总结

JSON 是很好用的数据格式&#xff0c;但是在不同语言环境之间切换还是有很多细节问题需要注意。此外&#xff0c;遵循传统规则&#xff0c;出现特例的时候&#xff0c;改变自身适应规则&#xff0c;而不是试图改变规则&#xff0c;这一条不一定适应所有问题&#xff0c;但对于那些已被公认的规则&#xff0c;请勿轻易挑战。

转:https://www.cnblogs.com/jiangzhaowei/p/6636527.html



推荐阅读
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 如何基于ggplot2构建相关系数矩阵热图以及一个友情故事
    本文介绍了如何在rstudio中安装ggplot2,并使用ggplot2构建相关系数矩阵热图。同时,通过一个友情故事,讲述了真爱难觅的故事背后的数据量化和皮尔逊相关系数的概念。故事中的小伙伴们在本科时参加各种考试,其中有些沉迷网络游戏,有些热爱体育,通过他们的故事,展示了不同兴趣和特长对学习和成绩的影响。 ... [详细]
author-avatar
星控-集中营_220
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有