Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / docs / source / XmlReader.html
1 <html>
2 <head>
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
4   <title>The source code</title>
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 </head>
8 <body  onload="prettyPrint();">
9     <pre class="prettyprint lang-js">/*!
10  * Ext JS Library 3.3.1
11  * Copyright(c) 2006-2010 Sencha Inc.
12  * licensing@sencha.com
13  * http://www.sencha.com/license
14  */
15 <div id="cls-Ext.data.XmlReader"></div>/**
16  * @class Ext.data.XmlReader
17  * @extends Ext.data.DataReader
18  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an XML document
19  * based on mappings in a provided {@link Ext.data.Record} constructor.</p>
20  * <p><b>Note</b>: that in order for the browser to parse a returned XML document, the Content-Type
21  * header in the HTTP response must be set to "text/xml" or "application/xml".</p>
22  * <p>Example code:</p>
23  * <pre><code>
24 var Employee = Ext.data.Record.create([
25    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it is the same as "name"
26    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
27 ]);
28 var myReader = new Ext.data.XmlReader({
29    totalProperty: "results", // The element which contains the total dataset size (optional)
30    record: "row",           // The repeated element which contains row information
31    idProperty: "id"         // The element within the row that provides an ID for the record (optional)
32    messageProperty: "msg"   // The element within the response that provides a user-feedback message (optional)
33 }, Employee);
34 </code></pre>
35  * <p>
36  * This would consume an XML file like this:
37  * <pre><code>
38 &lt;?xml version="1.0" encoding="UTF-8"?>
39 &lt;dataset>
40  &lt;results>2&lt;/results>
41  &lt;row>
42    &lt;id>1&lt;/id>
43    &lt;name>Bill&lt;/name>
44    &lt;occupation>Gardener&lt;/occupation>
45  &lt;/row>
46  &lt;row>
47    &lt;id>2&lt;/id>
48    &lt;name>Ben&lt;/name>
49    &lt;occupation>Horticulturalist&lt;/occupation>
50  &lt;/row>
51 &lt;/dataset>
52 </code></pre>
53  * @cfg {String} totalProperty The DomQuery path from which to retrieve the total number of records
54  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
55  * paged from the remote server.
56  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
57  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
58  * @cfg {String} successProperty The DomQuery path to the success attribute used by forms.
59  * @cfg {String} idPath The DomQuery path relative from the record element to the element that contains
60  * a record identifier value.
61  * @constructor
62  * Create a new XmlReader.
63  * @param {Object} meta Metadata configuration options
64  * @param {Object} recordType Either an Array of field definition objects as passed to
65  * {@link Ext.data.Record#create}, or a Record constructor object created using {@link Ext.data.Record#create}.
66  */
67 Ext.data.XmlReader = function(meta, recordType){
68     meta = meta || {};
69
70     // backwards compat, convert idPath or id / success
71     Ext.applyIf(meta, {
72         idProperty: meta.idProperty || meta.idPath || meta.id,
73         successProperty: meta.successProperty || meta.success
74     });
75
76     Ext.data.XmlReader.superclass.constructor.call(this, meta, recordType || meta.fields);
77 };
78 Ext.extend(Ext.data.XmlReader, Ext.data.DataReader, {
79     /**
80      * This method is only used by a DataProxy which has retrieved data from a remote server.
81      * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
82      * to contain a property called <tt>responseXML</tt> which refers to an XML document object.
83      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
84      * a cache of Ext.data.Records.
85      */
86     read : function(response){
87         var doc = response.responseXML;
88         if(!doc) {
89             throw {message: "XmlReader.read: XML Document not available"};
90         }
91         return this.readRecords(doc);
92     },
93
94     /**
95      * Create a data block containing Ext.data.Records from an XML document.
96      * @param {Object} doc A parsed XML document.
97      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
98      * a cache of Ext.data.Records.
99      */
100     readRecords : function(doc){
101         /**
102          * After any data loads/reads, the raw XML Document is available for further custom processing.
103          * @type XMLDocument
104          */
105         this.xmlData = doc;
106
107         var root    = doc.documentElement || doc,
108             q       = Ext.DomQuery,
109             totalRecords = 0,
110             success = true;
111
112         if(this.meta.totalProperty){
113             totalRecords = this.getTotal(root, 0);
114         }
115         if(this.meta.successProperty){
116             success = this.getSuccess(root);
117         }
118
119         var records = this.extractData(q.select(this.meta.record, root), true); // <-- true to return Ext.data.Record[]
120
121         // TODO return Ext.data.Response instance.  @see #readResponse
122         return {
123             success : success,
124             records : records,
125             totalRecords : totalRecords || records.length
126         };
127     },
128
129     /**
130      * Decode an XML response from server.
131      * @param {String} action [{@link Ext.data.Api#actions} create|read|update|destroy]
132      * @param {Object} response HTTP Response object from browser.
133      * @return {Ext.data.Response} An instance of {@link Ext.data.Response}
134      */
135     readResponse : function(action, response) {
136         var q = Ext.DomQuery,
137             doc = response.responseXML,
138             root = doc.documentElement || doc;
139
140         // create general Response instance.
141         var res = new Ext.data.Response({
142             action: action,
143             success : this.getSuccess(root),
144             message: this.getMessage(root),
145             data: this.extractData(q.select(this.meta.record, root) || q.select(this.meta.root, root), false),
146             raw: doc
147         });
148
149         if (Ext.isEmpty(res.success)) {
150             throw new Ext.data.DataReader.Error('successProperty-response', this.meta.successProperty);
151         }
152
153         // Create actions from a response having status 200 must return pk
154         if (action === Ext.data.Api.actions.create) {
155             var def = Ext.isDefined(res.data);
156             if (def && Ext.isEmpty(res.data)) {
157                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
158             }
159             else if (!def) {
160                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
161             }
162         }
163         return res;
164     },
165
166     getSuccess : function() {
167         return true;
168     },
169
170     /**
171      * build response-data extractor functions.
172      * @private
173      * @ignore
174      */
175     buildExtractors : function() {
176         if(this.ef){
177             return;
178         }
179         var s       = this.meta,
180             Record  = this.recordType,
181             f       = Record.prototype.fields,
182             fi      = f.items,
183             fl      = f.length;
184
185         if(s.totalProperty) {
186             this.getTotal = this.createAccessor(s.totalProperty);
187         }
188         if(s.successProperty) {
189             this.getSuccess = this.createAccessor(s.successProperty);
190         }
191         if (s.messageProperty) {
192             this.getMessage = this.createAccessor(s.messageProperty);
193         }
194         this.getRoot = function(res) {
195             return (!Ext.isEmpty(res[this.meta.record])) ? res[this.meta.record] : res[this.meta.root];
196         };
197         if (s.idPath || s.idProperty) {
198             var g = this.createAccessor(s.idPath || s.idProperty);
199             this.getId = function(rec) {
200                 var id = g(rec) || rec.id;
201                 return (id === undefined || id === '') ? null : id;
202             };
203         } else {
204             this.getId = function(){return null;};
205         }
206         var ef = [];
207         for(var i = 0; i < fl; i++){
208             f = fi[i];
209             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
210             ef.push(this.createAccessor(map));
211         }
212         this.ef = ef;
213     },
214
215     /**
216      * Creates a function to return some particular key of data from a response.
217      * @param {String} key
218      * @return {Function}
219      * @private
220      * @ignore
221      */
222     createAccessor : function(){
223         var q = Ext.DomQuery;
224         return function(key) {
225             if (Ext.isFunction(key)) {
226                 return key;
227             }
228             switch(key) {
229                 case this.meta.totalProperty:
230                     return function(root, def){
231                         return q.selectNumber(key, root, def);
232                     };
233                     break;
234                 case this.meta.successProperty:
235                     return function(root, def) {
236                         var sv = q.selectValue(key, root, true);
237                         var success = sv !== false && sv !== 'false';
238                         return success;
239                     };
240                     break;
241                 default:
242                     return function(root, def) {
243                         return q.selectValue(key, root, def);
244                     };
245                     break;
246             }
247         };
248     }(),
249
250     /**
251      * extracts values and type-casts a row of data from server, extracted by #extractData
252      * @param {Hash} data
253      * @param {Ext.data.Field[]} items
254      * @param {Number} len
255      * @private
256      * @ignore
257      */
258     extractValues : function(data, items, len) {
259         var f, values = {};
260         for(var j = 0; j < len; j++){
261             f = items[j];
262             var v = this.ef[j](data);
263             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
264         }
265         return values;
266     }
267 });</pre>    
268 </body>
269 </html>