e325149adfdd55ce2fb990d180578777a11baef6
[roojs1] / Roo / data / JsonReader.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12 /**
13  * @class Roo.data.JsonReader
14  * @extends Roo.data.DataReader
15  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16  * based on mappings in a provided Roo.data.Record constructor.
17  * 
18  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
19  * in the reply previously. 
20  * 
21  * <p>
22  * Example code:
23  * <pre><code>
24 var RecordDef = Roo.data.Record.create([
25     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
27 ]);
28 var myReader = new Roo.data.JsonReader({
29     totalProperty: "results",    // The property which contains the total dataset size (optional)
30     root: "rows",                // The property which contains an Array of row objects
31     id: "id"                     // The property within each row object that provides an ID for the record (optional)
32 }, RecordDef);
33 </code></pre>
34  * <p>
35  * This would consume a JSON file like this:
36  * <pre><code>
37 { 'results': 2, 'rows': [
38     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
39     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
40 }
41 </code></pre>
42  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
43  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
44  * paged from the remote server.
45  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
46  * @cfg {String} root name of the property which contains the Array of row objects.
47  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
48  * @cfg {Array} fields Array of field definition objects
49  * @constructor
50  * Create a new JsonReader
51  * @param {Object} meta Metadata configuration options
52  * @param {Object} recordType Either an Array of field definition objects,
53  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
54  */
55 Roo.data.JsonReader = function(meta, recordType){
56     
57     meta = meta || {};
58     // set some defaults:
59     Roo.applyIf(meta, {
60         totalProperty: 'total',
61         successProperty : 'success',
62         root : 'data',
63         id : 'id'
64     });
65     
66     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
67 };
68 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
69     
70     readerType : 'Json',
71     
72     /**
73      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
74      * Used by Store query builder to append _requestMeta to params.
75      * 
76      */
77     metaFromRemote : false,
78     /**
79      * This method is only used by a DataProxy which has retrieved data from a remote server.
80      * @param {Object} response The XHR object which contains the JSON data in its responseText.
81      * @return {Object} data A data block which is used by an Roo.data.Store object as
82      * a cache of Roo.data.Records.
83      */
84     read : function(response){
85         var json = response.responseText;
86        
87         var o = /* eval:var:o */ eval("("+json+")");
88         if(!o) {
89             throw {message: "JsonReader.read: Json object not found"};
90         }
91         
92         if(o.metaData){
93             
94             delete this.ef;
95             this.metaFromRemote = true;
96             this.meta = o.metaData;
97             this.recordType = Roo.data.Record.create(o.metaData.fields);
98             this.onMetaChange(this.meta, this.recordType, o);
99         }
100         return this.readRecords(o);
101     },
102
103     // private function a store will implement
104     onMetaChange : function(meta, recordType, o){
105
106     },
107
108     /**
109          * @ignore
110          */
111     simpleAccess: function(obj, subsc) {
112         return obj[subsc];
113     },
114
115         /**
116          * @ignore
117          */
118     getJsonAccessor: function(){
119         var re = /[\[\.]/;
120         return function(expr) {
121             try {
122                 return(re.test(expr))
123                     ? new Function("obj", "return obj." + expr)
124                     : function(obj){
125                         return obj[expr];
126                     };
127             } catch(e){}
128             return Roo.emptyFn;
129         };
130     }(),
131
132     /**
133      * Create a data block containing Roo.data.Records from an XML document.
134      * @param {Object} o An object which contains an Array of row objects in the property specified
135      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
136      * which contains the total size of the dataset.
137      * @return {Object} data A data block which is used by an Roo.data.Store object as
138      * a cache of Roo.data.Records.
139      */
140     readRecords : function(o){
141         /**
142          * After any data loads, the raw JSON data is available for further custom processing.
143          * @type Object
144          */
145         this.o = o;
146         var s = this.meta, Record = this.recordType,
147             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
148
149 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
150         if (!this.ef) {
151             if(s.totalProperty) {
152                     this.getTotal = this.getJsonAccessor(s.totalProperty);
153                 }
154                 if(s.successProperty) {
155                     this.getSuccess = this.getJsonAccessor(s.successProperty);
156                 }
157                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
158                 if (s.id) {
159                         var g = this.getJsonAccessor(s.id);
160                         this.getId = function(rec) {
161                                 var r = g(rec);  
162                                 return (r === undefined || r === "") ? null : r;
163                         };
164                 } else {
165                         this.getId = function(){return null;};
166                 }
167             this.ef = [];
168             for(var jj = 0; jj < fl; jj++){
169                 f = fi[jj];
170                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
171                 this.ef[jj] = this.getJsonAccessor(map);
172             }
173         }
174
175         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
176         if(s.totalProperty){
177             var vt = parseInt(this.getTotal(o), 10);
178             if(!isNaN(vt)){
179                 totalRecords = vt;
180             }
181         }
182         if(s.successProperty){
183             var vs = this.getSuccess(o);
184             if(vs === false || vs === 'false'){
185                 success = false;
186             }
187         }
188         var records = [];
189         for(var i = 0; i < c; i++){
190             var n = root[i];
191             var values = {};
192             var id = this.getId(n);
193             for(var j = 0; j < fl; j++){
194                 f = fi[j];
195                                 var v = this.ef[j](n);
196                                 if (!f.convert) {
197                                         Roo.log('missing convert for ' + f.name);
198                                         Roo.log(f);
199                                         continue;
200                                 }
201                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
202             }
203                         if (!Record) {
204                                 return {
205                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
206                                         success : false,
207                                         records : [],
208                                         totalRecords : 0
209                                 };
210                         }
211             var record = new Record(values, id);
212             record.json = n;
213             records[i] = record;
214         }
215         return {
216             raw : o,
217             success : success,
218             records : records,
219             totalRecords : totalRecords
220         };
221     },
222     // used when loading children.. @see loadDataFromChildren
223     toLoadData: function(rec)
224     {
225         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
226         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
227         return { data : data, total : data.length };
228         
229     }
230 });