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

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

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

關(guān)于本文

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

1. What is JSON?

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

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

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

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

2. Python <=> MongoDB

Python 與 MongoDB 之間的交互主要由現(xiàn)有的驅(qū)動庫提供支持,包括 PyMongo、Motor 等,而這些驅(qū)動所提供的接口都是非常友好的,我們不需要了解任何底層的實(shí)現(xiàn),只要對 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 中的索引項 _id 是通過 ObjectId("572df0b78a83851d5f24e2c1") 存儲的,而對應(yīng)的 Python 對象為 bson.objectid.ObjectId ,因此在查詢時需要以此對象的實(shí)例進(jìn)行:

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

3. Python <=> Ajax

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

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

于是迎來了第一個錯誤: TypeError: ObjectId('572df0b58a83851d5f24e2b1') is not JSON serializable 。追溯原因,雖然 Tornado 幫我們簡化了操作,但在像 HTTP 中寫入字典類型時仍然需要經(jīng)歷一次 json.dumps(user) 操作,而對于 json.dumps 來說, 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))

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

JSONEncoder.encode 之后字典類型被轉(zhuǎn)換成字符串,寫入 HTTP 之后 Content-Type 變?yōu)?text/html ,這時前端將認(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);
})

問題暫時解決了,在整個過程中 JSON 的變換是這樣的:

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

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

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

這就是在遇到問題是只著眼解決眼前錯誤導(dǎo)致后續(xù)一連串改動所帶來的弊病。我們沿著上面 JSON 變換的鏈條向上追溯,看有沒有更好的解決方案。很簡單, 遵循傳統(tǒng)規(guī)則,出現(xiàn)特例的時候,改變自身適應(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

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

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

# 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é)的問題一樣,模板引擎渲染過程與 HTTP 傳輸過程是類似的,不同的是在模板中字符串變量就是純粹的值(沒有引號),因此完全可以用生成 JavaScript 腳本文件的形式渲染變量而無需顧慮特殊字符(下面的 {% raw ... %} 是 Tornado 模板用于防止特殊符號被 HTML 編碼的語法):

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

總結(jié)

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

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

相關(guān)文章

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

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

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

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

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

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

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

    第一篇初識bootstrap

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

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

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

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

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

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

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

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

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

    Three.js利用dat.GUI如何簡化試驗流程詳解

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

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

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

最新評論

金昌市| 博白县| 金平| 陕西省| 惠东县| 宁德市| 镇原县| 胶州市| 巴彦县| 台中县| 襄城县| 泸定县| 昌黎县| 拜泉县| 舞钢市| 大关县| 盐池县| 湛江市| 永兴县| 澄城县| 开平市| 商城县| 和田县| 武汉市| 吴川市| 海城市| 河北省| 澄江县| 濮阳市| 南丰县| 南宁市| 洛阳市| 贡山| 丰台区| 革吉县| 泗水县| 瑞安市| 林西县| 荆州市| 庄浪县| 罗源县|