361b361ecc8d2db8d862da2d79e8b45db8686bde
[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     /**
71      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
72      * Used by Store query builder to append _requestMeta to params.
73      * 
74      */
75     metaFromRemote : false,
76     /**
77      * This method is only used by a DataProxy which has retrieved data from a remote server.
78      * @param {Object} response The XHR object which contains the JSON data in its responseText.
79      * @return {Object} data A data block which is used by an Roo.data.Store object as
80      * a cache of Roo.data.Records.
81      */
82     read : function(response){
83         var json = response.responseText;
84        
85         var o = /* eval:var:o */ eval("("+json+")");
86         if(!o) {
87             throw {message: "JsonReader.read: Json object not found"};
88         }
89         
90         if(o.metaData){
91             
92             delete this.ef;
93             this.metaFromRemote = true;
94             this.meta = o.metaData;
95             this.recordType = Roo.data.Record.create(o.metaData.fields);
96             this.onMetaChange(this.meta, this.recordType, o);
97         }
98         return this.readRecords(o);
99     },
100
101     // private function a store will implement
102     onMetaChange : function(meta, recordType, o){
103
104     },
105
106     /**
107          * @ignore
108          */
109     simpleAccess: function(obj, subsc) {
110         return obj[subsc];
111     },
112
113         /**
114          * @ignore
115          */
116     getJsonAccessor: function(){
117         var re = /[\[\.]/;
118         return function(expr) {
119             try {
120                 return(re.test(expr))
121                     ? new Function("obj", "return obj." + expr)
122                     : function(obj){
123                         return obj[expr];
124                     };
125             } catch(e){}
126             return Roo.emptyFn;
127         };
128     }(),
129
130     /**
131      * Create a data block containing Roo.data.Records from an XML document.
132      * @param {Object} o An object which contains an Array of row objects in the property specified
133      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
134      * which contains the total size of the dataset.
135      * @return {Object} data A data block which is used by an Roo.data.Store object as
136      * a cache of Roo.data.Records.
137      */
138     readRecords : function(o){
139         /**
140          * After any data loads, the raw JSON data is available for further custom processing.
141          * @type Object
142          */
143         this.o = o;
144         var s = this.meta, Record = this.recordType,
145             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
146
147 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
148         if (!this.ef) {
149             if(s.totalProperty) {
150                     this.getTotal = this.getJsonAccessor(s.totalProperty);
151                 }
152                 if(s.successProperty) {
153                     this.getSuccess = this.getJsonAccessor(s.successProperty);
154                 }
155                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
156                 if (s.id) {
157                         var g = this.getJsonAccessor(s.id);
158                         this.getId = function(rec) {
159                                 var r = g(rec);  
160                                 return (r === undefined || r === "") ? null : r;
161                         };
162                 } else {
163                         this.getId = function(){return null;};
164                 }
165             this.ef = [];
166             for(var jj = 0; jj < fl; jj++){
167                 f = fi[jj];
168                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
169                 this.ef[jj] = this.getJsonAccessor(map);
170             }
171         }
172
173         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
174         if(s.totalProperty){
175             var vt = parseInt(this.getTotal(o), 10);
176             if(!isNaN(vt)){
177                 totalRecords = vt;
178             }
179         }
180         if(s.successProperty){
181             var vs = this.getSuccess(o);
182             if(vs === false || vs === 'false'){
183                 success = false;
184             }
185         }
186         var records = [];
187         for(var i = 0; i < c; i++){
188                 var n = root[i];
189             var values = {};
190             var id = this.getId(n);
191             for(var j = 0; j < fl; j++){
192                 f = fi[j];
193             var v = this.ef[j](n);
194             if (!f.convert) {
195                 Roo.log('missing convert for ' + f.name);
196                 Roo.log(f);
197                 continue;
198             }
199             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
200             }
201             var record = new Record(values, id);
202             record.json = n;
203             records[i] = record;
204         }
205         return {
206             raw : o,
207             success : success,
208             records : records,
209             totalRecords : totalRecords
210         };
211     }
212 });