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