嘿,別看關鍵就在這兒,事實上,它的代碼很少的哦。加上注釋才219行。研究研究。
有個事要說一下:DataProxy的子類呢,都有一個load來加載數據,DataReader的子類呢,都有一個read來讀取數據。
而Ext.data.JsonReader有兩個關鍵函數:read、readRecords。好了。來研究一下。
Ext.data.JsonReader = function(meta, recordType){
meta = meta || {};
Ext.data.JsonReader.superclass.constructor.call(this, meta, recordType || meta.fields);
};
這是構造函數。簡單。meta是數據格式定義,recordType是記錄類型。其中recordType可以是一個定義記錄的數組,也可以不傳,而把記錄的各個字段的定義放到meta中的fields字段中。且看它對父類構造函數的調用:
Ext.data.DataReader = function(meta, recordType){
this.meta = meta;
this.recordType = Ext.isArray(recordType) ?
Ext.data.Record.create(recordType) : recordType;
};
Ext.data.DataReader.prototype = { };
這下全明白了吧。recordType可以是記錄類型,可以是字段定義數組,還可以不傳。
所以,構造函數就是定義兩個屬性:meta、recordType。這兩東西后面有用。
這個meta、recordType組成如何?這個必須說明,不然,這個類也就沒法用了。
meta:
totalProperty json數據中,保存總記錄數的屬性
successProperty json數據中,保存是否返回成功的屬性名
root json數據中,保存記錄集的屬性的屬性名
id json數據中,記錄中主鍵所對應的列的屬性名
recordType:
這個東西,事實上要去看Ext.data.Record的create函數的文檔,我且把它翻譯一下,如下:
create( [Array o] ) : function
創建包含指定字段結構的繼承自Ext.data.Record的類。靜態方法。
參數:
o : Array
一個定義記錄結構的字段信息數組。每個數組元素包含name,其他可選的有:mapping、type。通過它們,可以讓Ext.data.Reader從一個數據對象中獲取各字段的值。每個字段定義對象都可能包含如下屬性:
name : String
在記錄中標志一個字段的名字。它通常用于引用指定字段,例如,在定義Ext.grid.ColumnModel的dataIndex屬性時,要傳過去的。
mapping : String
當在Ext.data.Reader中創建記錄時,如何將json對象中指定屬性值映射到此字段。
type : String
字段的類型,可能值為:
auto(默認值,沒有任何轉化)、string、int、float、boolean、date
sortType : Mixed
Ext.data.SortTypes中的一個成員。
sortDir : String
排序方式,"ASC"或者"DESC"。
convert : Function
如果要對這個字段的值進行一些物殊處理,這時需要一個能定制的回調,用它來手工處理值。它的參數如下:
v : Mixed
通過mapping映射找到的值。已從json中取出來的。
rec : Mixed
在json中的,對應于此記錄的json對象。
dateFormat : String
用于Date.parseDate函數的格式化字符串。
defaultValue : Mixed
當字段值在原數據中不存在時所取的默認值,默認為空字符串。
用法:
var TopicRecord = Ext.data.Record.create([
{name: 'title', mapping: 'topic_title'},
{name: 'author', mapping: 'username'},
{name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
{name: 'lastPost', mapping: 'post_time', type: 'date'},
{name: 'lastPoster', mapping: 'user2'},
{name: 'excerpt', mapping: 'post_text'}
]);
var myNewRecord = new TopicRecord({
title: 'Do my job please',
author: 'noobie',
totalPosts: 1,
lastPost: new Date(),
lastPoster: 'Animal',
excerpt: 'No way dude!'
});
myStore.add(myNewRecord);
不過,JsonReader有一些細節問題,還要細究。待某家一一道來。
構造函數已講,下面依代碼順序講解了。
read : function(response){
var json = response.responseText;
var o = eval("("+json+")");
if(!o) {
throw {message: "JsonReader.read: Json object not found"};
}
return this.readRecords(o);
},
這個是整個JsonReader的關鍵所在了。君可找到Ext.data.HttpProxy中的loadResponse函數,里面有這么一行代碼:
result = o.reader.read(response);
可見,是proxy里面調用reader.read方法才得以取出結果集的。這是要表明:read乃JsonReader三軍中軍之所在。 read又調用readRecords,read把json字符串轉化為對象然后交給readRecords。這個本無不妥,但是,asp.net中,它的結果有點曲折,結果是放在o.d中,而不能直接從o中取得。所以,事實上應當這么寫:this.readRecords(o.d)。 這就成了。繼續往下面看:
onMetaChange : function(meta, recordType, o){
}
這個函數說是要由store實現的,現在不知道它的用處。還往下看:
simpleAccess: function(obj, subsc) {
return obj[subsc];
},
getJsonAccessor: function(){
var re = /[/[/.]/;
return function(expr) {
try {
return(re.test(expr))
? new Function("obj", "return obj." + expr)
: function(obj){
return obj[expr];
};
} catch(e){}
return Ext.emptyFn;
};
}(),
取一對象的屬性有兩種方法,前面都已提及:
一、obj.xxxx
二、obj[xxxx]
這兩種都行。但是,如果傳過來一個對象,已知其對象的引用obj,但是有的只是它的屬性名的字符串,這時就可以用第二種方法取出,但是,如屬性名中含[],那么就不大方便了,又或者是屬性又帶屬性,這事也只能用第一種方法。這兩個函數正是為事而來。且看那getJsonAccessor,著實巧妙,函數返回一函數,這不是巧妙之處,這個我以前就見識了,關鍵在于new Function("obj","return "obj."+expr) 。多么巧妙啊。此之中巧,不足以言語道哉。
這下面就是真正的好戲了,看一看readRecords函數。
this.jsonData = o;
if(o.metaData){
delete this.ef;
this.meta = o.metaData;
this.recordType = Ext.data.Record.create(o.metaData.fields);
this.onMetaChange(this.meta, this.recordType, o);
}
定義一個jsonData屬性以保存原始json對象。然后如果傳過的json對象中就有metaData。那么,就用它自帶的meta來取代JsonReader構造函數中所傳入的meta。以原來自帶的為主。這個功能方檔未曾提及,但我輩不可不察也。
var s = this.meta, Record = this.recordType,
f = Record.prototype.fields, fi = f.items, fl = f.length;
有人不理解了,為什么非得這樣呢?這是節省帶寬啊。如果這些東西以后多說現幾次,那么每個用戶都要多下載一些東西,成千上萬人能節省多少啊。
if (!this.ef) {
if(s.totalProperty) {
this.getTotal = this.getJsonAccessor(s.totalProperty);
}
if(s.successProperty) {
this.getSuccess = this.getJsonAccessor(s.successProperty);
}
this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
if (s.id) {
var g = this.getJsonAccessor(s.id);
this.getId = function(rec) {
var r = g(rec);
return (r === undefined || r === "") ? null : r;
};
} else {
this.getId = function(){return null;};
}
this.ef = [];
for(var i = 0; i < fl; i++){
f = fi[i];
var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
this.ef[i] = this.getJsonAccessor(map);
}
}
因為要根據meta.id、meta.root。這兩值都是字符串,這就要用到前面定義的getJsonAccessor函數了。這兒正是來生成幾個取json對象中屬性的函數,如:getTotal、getSuccess、getRoot、getId、ef數組,一個ef數組就解決了屬性映射的問題,真是漂亮。
var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
if(s.totalProperty){
var v = parseInt(this.getTotal(o), 10);
if(!isNaN(v)){
totalRecords = v;
}
}
if(s.successProperty){
var v = this.getSuccess(o);
if(v === false || v === 'false'){
success = false;
}
}
這兒是求totalRecords、success。有一事要注意:其中:
c = root.length, totalRecords = c
這上c后面要用來循環的,而totalRecords是要返回的,而后,又求了totalRecords,這個意思是:如果結果中沒有 totalProperty這一屬性,那么就自動求取,如果存在,則以定義的totalProperty為主,由此可見,totalProperty是可有可無的。這個問題文檔不曾見之。諸位可無憂矣。
var records = [];
for(var i = 0; i < c; i++){
var n = root[i];
var values = {};
var id = this.getId(n);
for(var j = 0; j < fl; j++){
f = fi[j];
var v = this.ef[j](n);
values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, n);
}
var record = new Record(values, id);
record.json = n;
records[i] = record;
}
return {
success : success,
records : records,
totalRecords : totalRecords
};
這是剩余的代碼了,由for(var i = 0; i < c; i++)可知,循環的時候還是用root.length的。而不是totalProperty。這個要分清,事實上,totalProperty只是直接返回罷了,未做任何改動。里面就轉化成Record了。其中,這個ef數組用得巧妙。類型轉化用了convert。這個東西前文已講,不足道哉。
var record = new Record(values, id);
id=this.getId(n),可見啦,id并非前文所說的主鍵,它只是一個用來做客戶端唯一編號的東西,如對此有疑,可見于Ext.data.Record類。
record.json = n,json這個屬性我在Ext.data.Record類中并未曾得見,諸君注意了,這個東西也許會有用。另外,readRecords返回的不只是一個records數組,而是一個json對象,包含success、records、totalRecords。
至此,JsonReader源代碼分析完畢,呵呵,因為這個類代碼量較少,故講得詳細。