Upgrade to ExtJS 3.2.0 - Released 03/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.2.0
11  * Copyright(c) 2006-2010 Ext JS, Inc.
12  * licensing@extjs.com
13  * http://www.extjs.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
139         // create general Response instance.
140         var res = new Ext.data.Response({
141             action: action,
142             success : this.getSuccess(doc),
143             message: this.getMessage(doc),
144             data: this.extractData(q.select(this.meta.record, doc) || q.select(this.meta.root, doc), false),
145             raw: doc
146         });
147
148         if (Ext.isEmpty(res.success)) {
149             throw new Ext.data.DataReader.Error('successProperty-response', this.meta.successProperty);
150         }
151
152         // Create actions from a response having status 200 must return pk
153         if (action === Ext.data.Api.actions.create) {
154             var def = Ext.isDefined(res.data);
155             if (def && Ext.isEmpty(res.data)) {
156                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
157             }
158             else if (!def) {
159                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
160             }
161         }
162         return res;
163     },
164
165     getSuccess : function() {
166         return true;
167     },
168
169     /**
170      * build response-data extractor functions.
171      * @private
172      * @ignore
173      */
174     buildExtractors : function() {
175         if(this.ef){
176             return;
177         }
178         var s       = this.meta,
179             Record  = this.recordType,
180             f       = Record.prototype.fields,
181             fi      = f.items,
182             fl      = f.length;
183
184         if(s.totalProperty) {
185             this.getTotal = this.createAccessor(s.totalProperty);
186         }
187         if(s.successProperty) {
188             this.getSuccess = this.createAccessor(s.successProperty);
189         }
190         if (s.messageProperty) {
191             this.getMessage = this.createAccessor(s.messageProperty);
192         }
193         this.getRoot = function(res) {
194             return (!Ext.isEmpty(res[this.meta.record])) ? res[this.meta.record] : res[this.meta.root];
195         };
196         if (s.idPath || s.idProperty) {
197             var g = this.createAccessor(s.idPath || s.idProperty);
198             this.getId = function(rec) {
199                 var id = g(rec) || rec.id;
200                 return (id === undefined || id === '') ? null : id;
201             };
202         } else {
203             this.getId = function(){return null;};
204         }
205         var ef = [];
206         for(var i = 0; i < fl; i++){
207             f = fi[i];
208             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
209             ef.push(this.createAccessor(map));
210         }
211         this.ef = ef;
212     },
213
214     /**
215      * Creates a function to return some particular key of data from a response.
216      * @param {String} key
217      * @return {Function}
218      * @private
219      * @ignore
220      */
221     createAccessor : function(){
222         var q = Ext.DomQuery;
223         return function(key) {
224             switch(key) {
225                 case this.meta.totalProperty:
226                     return function(root, def){
227                         return q.selectNumber(key, root, def);
228                     };
229                     break;
230                 case this.meta.successProperty:
231                     return function(root, def) {
232                         var sv = q.selectValue(key, root, true);
233                         var success = sv !== false && sv !== 'false';
234                         return success;
235                     };
236                     break;
237                 default:
238                     return function(root, def) {
239                         return q.selectValue(key, root, def);
240                     };
241                     break;
242             }
243         };
244     }(),
245
246     /**
247      * extracts values and type-casts a row of data from server, extracted by #extractData
248      * @param {Hash} data
249      * @param {Ext.data.Field[]} items
250      * @param {Number} len
251      * @private
252      * @ignore
253      */
254     extractValues : function(data, items, len) {
255         var f, values = {};
256         for(var j = 0; j < len; j++){
257             f = items[j];
258             var v = this.ef[j](data);
259             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
260         }
261         return values;
262     }
263 });</pre>    
264 </body>
265 </html>