最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JSON 的正確用法探討:Pyhong、MongoDB、JavaScript與Ajax

 更新時(shí)間:2016年05月15日 11:26:26   作者:rainy  
這篇文章主要介紹了JSON 的正確用法探討:Pyhong、MongoDB、JavaScript與Ajax的相關(guān)資料,具有參考借鑒價(jià)值,需要的朋友一起學(xué)習(xí)吧

關(guān)于本文

本文主要總結(jié)網(wǎng)站編寫以來(lái)在傳遞 JSON 數(shù)據(jù)方面遇到的一些問(wèn)題以及目前采用的解決方案。網(wǎng)站數(shù)據(jù)庫(kù)采用 MongoDB,后端是 Python,前端采用“半分離”形式的 Riot.js,所謂半分離,是說(shuō)第一頁(yè)數(shù)據(jù)是通過(guò)服務(wù)器端的模板引擎直接渲染到 HTML 中,從而避免首頁(yè)兩次加載的問(wèn)題,而其它動(dòng)態(tài)內(nèi)容則采用 Ajax 加載。整個(gè)流程中數(shù)據(jù)都是通過(guò) JSON 格式傳遞的,但是在不同的環(huán)節(jié)中需要采用不同的方式并遇到一些不同的問(wèn)題,本文主要做記錄、總結(jié)。

1. What is JSON?

JSON(JavaScript Object Notation) 是一種由道格拉斯·克羅克福特構(gòu)想設(shè)計(jì)、輕量級(jí)的數(shù)據(jù)交換語(yǔ)言,它的前輩 XML 可能更早被人們所熟知。當(dāng)然 JSON 并不是為了取代 XML 而存在的,只是相比于 XML 它更小巧、更適合在網(wǎng)頁(yè)開(kāi)發(fā)中用作數(shù)據(jù)傳遞(JSON 之于 JavaScript 就像 XML 之于 Lisp)。從名字上可以看出,JSON 的格式符合 JavaScript 語(yǔ)言中“對(duì)象”的語(yǔ)法格式,除了 JavaScript 之外,很多其他語(yǔ)言中也具有類似的類型,例如 Python 中的字典( dict ),除了編程語(yǔ)言之外,一些基于文檔存儲(chǔ)的 NoSQL 非關(guān)系型數(shù)據(jù)庫(kù)也選擇 JSON 作為其數(shù)據(jù)存儲(chǔ)格式,例如 MongoDB。

總的來(lái)說(shuō),JSON 定義一種標(biāo)記格式,可以非常方便地在編程語(yǔ)言中的變量數(shù)據(jù)與字符串文本數(shù)據(jù)之間相互轉(zhuǎn)換。JSON 描述的數(shù)據(jù)結(jié)構(gòu)包括以下這幾種形式:

對(duì)象: {key: value}
列表: [obj, obj,...]
字符串: "string"
數(shù)字:數(shù)字
布爾值: true / false

了解了 JSON 的基本概念之后,下面分別針對(duì)上圖中的幾個(gè)數(shù)據(jù)交互環(huán)節(jié)進(jìn)行總結(jié)。

2. Python <=> MongoDB

Python 與 MongoDB 之間的交互主要由現(xiàn)有的驅(qū)動(dòng)庫(kù)提供支持,包括 PyMongo、Motor 等,而這些驅(qū)動(dòng)所提供的接口都是非常友好的,我們不需要了解任何底層的實(shí)現(xiàn),只要對(duì) Python 原生的字典類型進(jìn)行操作即可:

import motor 
client = motor.motor_tornado.MotorClient() 
db = client['test']
user_col = db['user'] 
user_col.insert(dict( 
name = 'Yu',
is_admin = True,
))

唯一需要注意的是 MongoDB 中的索引項(xiàng) _id 是通過(guò) ObjectId("572df0b78a83851d5f24e2c1") 存儲(chǔ)的,而對(duì)應(yīng)的 Python 對(duì)象為 bson.objectid.ObjectId ,因此在查詢時(shí)需要以此對(duì)象的實(shí)例進(jìn)行:

from bson.objectid import ObjectId 
user = db.user.find_one(dict( 
_id = ObjectId("572df0b78a83851d5f24e2c1")
))

3. Python <=> Ajax

前端與后端之間的數(shù)據(jù)交流比較常用的是通過(guò) Ajax 完成,這時(shí)遇到了第一個(gè)不大不小的坑。在之前的一篇文章中,我總結(jié)了 一次 Python 編碼的坑 ,我們知道 HTTP 傳遞過(guò)程中肯定不存在 JSON/XML ,一切都是二進(jìn)制數(shù)據(jù),但是我們可以選擇讓前端用什么樣的方式解讀這些數(shù)據(jù),即通過(guò)設(shè)定 Header 中的 Content-Type ,一般傳遞 JSON 數(shù)據(jù)時(shí)將其設(shè)定為 Content-Type: application/json ,在 Tornado 最新版本中,只需要直接寫入字典類型即可:

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

于是迎來(lái)了第一個(gè)錯(cuò)誤: TypeError: ObjectId('572df0b58a83851d5f24e2b1') is not JSON serializable 。追溯原因,雖然 Tornado 幫我們簡(jiǎn)化了操作,但在像 HTTP 中寫入字典類型時(shí)仍然需要經(jīng)歷一次 json.dumps(user) 操作,而對(duì)于 json.dumps 來(lái)說(shuō), ObjectId 類型是非法的。于是我選擇了最直觀的解決方案:

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 = await self.db.user.find_one({})
self.write(JSONEncoder.encode(user))

這次不會(huì)再出錯(cuò)了,我們自己的 JSONEncoder 可以應(yīng)對(duì) ObjectId 了,但另一個(gè)問(wèn)題也出現(xiàn)了:

JSONEncoder.encode 之后字典類型被轉(zhuǎn)換成字符串,寫入 HTTP 之后 Content-Type 變?yōu)?text/html ,這時(shí)前端將認(rèn)為接收的數(shù)據(jù)為字符串而不是可用的 JavaScript Object。當(dāng)然還有進(jìn)一步的彌補(bǔ)方案,那就是前端再進(jìn)行一次轉(zhuǎn)換:

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

問(wèn)題暫時(shí)解決了,在整個(gè)過(guò)程中 JSON 的變換是這樣的:

Python ==> json.dumps ==> HTTP ==> JavaScript ==> JSON.parse 
dict ==> str ==> binary ==> string ==> Object 

結(jié)果第二個(gè)問(wèn)題來(lái)了,當(dāng)數(shù)據(jù)中存在一些特殊字符時(shí), JSON.parse 將出現(xiàn)錯(cuò)誤:

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

這就是在遇到問(wèn)題是只著眼解決眼前錯(cuò)誤導(dǎo)致后續(xù)一連串改動(dòng)所帶來(lái)的弊病。我們沿著上面 JSON 變換的鏈條向上追溯,看有沒(méi)有更好的解決方案。很簡(jiǎn)單, 遵循傳統(tǒng)規(guī)則,出現(xiàn)特例的時(shí)候,改變自身適應(yīng)規(guī)則,而不是改變規(guī)則 :

# Handler
async def post(self): 
user = await self.db.user.find_one({})
user['_id'] = str(user['_id'])
self.write(user)

當(dāng)然,如果是多條數(shù)據(jù)的列表形式,還需要進(jìn)一步改造:

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

4. Python <=> HTML+Riot.js

如果上面的問(wèn)題可以通過(guò) 遵守規(guī)則 來(lái)解決,那么接下來(lái)這個(gè)問(wèn)題就是一個(gè)挑戰(zhàn)規(guī)則的故事。除去 Ajax 動(dòng)態(tài)加載部分,網(wǎng)頁(yè)上的其他數(shù)據(jù)是通過(guò)后端模板引擎渲染得來(lái)的,也就是說(shuō)是 Hard-coding 為 HTML 的。在瀏覽器加載并解析這個(gè) HTML 文件之前它們只是純文本文件,而我們需要的是直接將數(shù)據(jù)塞僅 <script> 標(biāo)簽在瀏覽器運(yùn)行 JavaScript 時(shí)直接可用。嚴(yán)格意義上來(lái)說(shuō)這并不算是 JSON 的應(yīng)用,而是 Python 的 dict 與 JavaScript 的 Object 之間的直接轉(zhuǎn)換,常規(guī)的方法應(yīng)該這樣寫:

# Handler
async def get(self): 
users = self.db.get_top_users()
render_data = dict(
users = users
)
self.render('users.html', **render_data)
<!-- HTML + Riot.js --> 
<app></app> 
<script> 
riot.mount('app', {
users: [
{% for user in users %}
{ name: "{{ user['name']}}", is_admin: "{{ user['is_admin']}}" },
{% end %}
],
})
</script> 

這樣寫是對(duì)的,但是要解決上面提到的 ObjectId() 問(wèn)題還是需要一些額外的處理(尤其是引號(hào)問(wèn)題)。另外為了解決 ObjectId 的問(wèn)題我還嘗試了一種比較蠢的方法(在上面的 JSON.parse 遇到錯(cuò)誤之前):

# Handler
async def get(self): 
users = self.db.get_top_users()
render_data = dict(
users = JSONEncoder.encode(users)
)
self.render('users.html', **render_data)
<!-- HTML + Riot.js --> 
<app></app> 
<script> 
riot.mount('app', {
users: JSON.parse('{{ users }}'),
})
</script> 

其實(shí)跟第 3 小節(jié)的問(wèn)題一樣,模板引擎渲染過(guò)程與 HTTP 傳輸過(guò)程是類似的,不同的是在模板中字符串變量就是純粹的值(沒(méi)有引號(hào)),因此完全可以用生成 JavaScript 腳本文件的形式渲染變量而無(wú)需顧慮特殊字符(下面的 {% raw ... %} 是 Tornado 模板用于防止特殊符號(hào)被 HTML 編碼的語(yǔ)法):

<!-- HTML + Riot.js --> 
<app></app> 
<script> 
riot.mount('app', {
users: {% raw users %}),
})
</script> 

總結(jié)

JSON 是很好用的數(shù)據(jù)格式,但是在不同語(yǔ)言環(huán)境之間切換還是有很多細(xì)節(jié)問(wèn)題需要注意。此外, 遵循傳統(tǒng)規(guī)則,出現(xiàn)特例的時(shí)候,改變自身適應(yīng)規(guī)則,而不是試圖改變規(guī)則 ,這一條不一定適應(yīng)所有問(wèn)題,但對(duì)于那些已被公認(rèn)的規(guī)則,請(qǐng)勿輕易挑戰(zhàn)。

以上所述是小編給大家介紹的JSON 的正確用法探討:Pyhong、MongoDB、JavaScript與Ajax的相關(guān)知識(shí),希望對(duì)大家有所幫助,如果大家想了解更多資訊敬請(qǐng)關(guān)注腳本之家網(wǎng)站!

相關(guān)文章

  • javascript實(shí)現(xiàn)導(dǎo)航欄分頁(yè)效果

    javascript實(shí)現(xiàn)導(dǎo)航欄分頁(yè)效果

    這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)導(dǎo)航欄分頁(yè)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • 一文詳解MySQL5.7與MySQL8之間的區(qū)別

    一文詳解MySQL5.7與MySQL8之間的區(qū)別

    MySQL作為最常用的開(kāi)源關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng)之一,一直在不斷發(fā)展和改進(jìn),其中,MySQL 5.7和MySQL 8是兩個(gè)備受關(guān)注的版本,它們之間存在一些關(guān)鍵的差異,本文將深入探討這兩個(gè)版本之間的主要差異,需要的朋友可以參考下
    2023-11-11
  • JavaScript操作Oracle數(shù)據(jù)庫(kù)示例

    JavaScript操作Oracle數(shù)據(jù)庫(kù)示例

    這篇文章主要介紹了JavaScript操作Oracle數(shù)據(jù)庫(kù)示例,本文使用ActiveXObject實(shí)現(xiàn)訪問(wèn)Oracle數(shù)據(jù)庫(kù),需要的朋友可以參考下
    2015-03-03
  • 第一篇初識(shí)bootstrap

    第一篇初識(shí)bootstrap

    Bootstrap是twitter的工程師在業(yè)余時(shí)間開(kāi)發(fā)的,是一款目前非常流行的前端框架。本文給大家介紹第一篇初識(shí)bootstrap的相關(guān)知識(shí),非常不錯(cuò)具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧
    2016-06-06
  • JavaScript鼠標(biāo)拖拽事件詳解

    JavaScript鼠標(biāo)拖拽事件詳解

    這篇文章主要為大家詳細(xì)介紹了JavaScript鼠標(biāo)拖拽事件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • 淺聊一下JavaScript中的LHS和RHS查詢

    淺聊一下JavaScript中的LHS和RHS查詢

    在日常編碼中,我們通常關(guān)注代碼的邏輯和功能,但很少深入思考編譯器在幕后的工作,今天我們將學(xué)習(xí)一下LHS(Left-Hand Side)和RHS(Right-Hand Side)查詢,它們?cè)贘avaScript編譯和執(zhí)行中的關(guān)鍵作用,以及在我們的日常開(kāi)發(fā)中是如何發(fā)揮作用的,需要的朋友可以參考下
    2023-11-11
  • javascript操作cookie方法函數(shù)集合

    javascript操作cookie方法函數(shù)集合

    很多朋友需要使用到cookies操作,腳本之家以前也發(fā)表了不少相關(guān)的文章。這篇文章,雖然亂了點(diǎn),但知識(shí)點(diǎn)比較多。
    2010-07-07
  • 妙用緩存調(diào)用鏈實(shí)現(xiàn)JS方法的重載

    妙用緩存調(diào)用鏈實(shí)現(xiàn)JS方法的重載

    方法重載是指在一個(gè)類中定義多個(gè)同名的方法,但要求每個(gè)方法具有不同的參數(shù)的類型或參數(shù)的個(gè)數(shù)。簡(jiǎn)而言之就是:方法重載就是方法名稱重復(fù),加載參數(shù)不同
    2018-04-04
  • Three.js利用dat.GUI如何簡(jiǎn)化試驗(yàn)流程詳解

    Three.js利用dat.GUI如何簡(jiǎn)化試驗(yàn)流程詳解

    dat.gui可以方便地向場(chǎng)景中添加控制條,隨時(shí)調(diào)整參數(shù)。下面這篇文章主要給大家介紹了關(guān)于Three.js利用dat.GUI如何簡(jiǎn)化試驗(yàn)流程的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-09-09
  • RGB顏色值轉(zhuǎn)HTML十六進(jìn)制(HEX)代碼的JS函數(shù)

    RGB顏色值轉(zhuǎn)HTML十六進(jìn)制(HEX)代碼的JS函數(shù)

    轉(zhuǎn)到固定長(zhǎng)度的十六進(jìn)制字符串,不夠則補(bǔ)0 javascript找出一個(gè)背景色的數(shù)值,只好自己解析
    2009-04-04

最新評(píng)論

休宁县| 临安市| 高阳县| 定远县| 乡宁县| 岚皋县| 宝应县| 临武县| 博爱县| 青岛市| 阿巴嘎旗| 伊川县| 达日县| 黔西| 平远县| 昭觉县| 长武县| 新宾| 西乡县| 会宁县| 湘潭市| 肃宁县| 沾化县| 霍城县| 汪清县| 涟源市| 建昌县| 贵溪市| 泰顺县| 长宁县| 东方市| 绥江县| 金沙县| 贵定县| 平昌县| 缙云县| 禹城市| 南宫市| 赫章县| 澎湖县| 山阳县|