Xml2.html
11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
<script type="text/javascript">
function highlight() {
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
}
</script>
</head>
<body onload="prettyPrint(); highlight();">
<pre class="prettyprint lang-js"><span id='Ext-data-reader-Xml'>/**
</span> * @author Ed Spencer
*
* The XML Reader is used by a Proxy to read a server response that is sent back in XML format. This usually happens as
* a result of loading a Store - for example we might create something like this:
*
* Ext.define('User', {
* extend: 'Ext.data.Model',
* fields: ['id', 'name', 'email']
* });
*
* var store = Ext.create('Ext.data.Store', {
* model: 'User',
* proxy: {
* type: 'ajax',
* url : 'users.xml',
* reader: {
* type: 'xml',
* record: 'user',
* root: 'users'
* }
* }
* });
*
* The example above creates a 'User' model. Models are explained in the {@link Ext.data.Model Model} docs if you're not
* already familiar with them.
*
* We created the simplest type of XML Reader possible by simply telling our {@link Ext.data.Store Store}'s {@link
* Ext.data.proxy.Proxy Proxy} that we want a XML Reader. The Store automatically passes the configured model to the
* Store, so it is as if we passed this instead:
*
* reader: {
* type : 'xml',
* model: 'User',
* record: 'user',
* root: 'users'
* }
*
* The reader we set up is ready to read data from our server - at the moment it will accept a response like this:
*
* <?xml version="1.0" encoding="UTF-8"?>
* <users>
* <user>
* <id>1</id>
* <name>Ed Spencer</name>
* <email>ed@sencha.com</email>
* </user>
* <user>
* <id>2</id>
* <name>Abe Elias</name>
* <email>abe@sencha.com</email>
* </user>
* </users>
*
* First off there's {@link #root} option to define the root node `<users>` (there should be only one in a well-formed
* XML document). Then the XML Reader uses the configured {@link #record} option to pull out the data for each record -
* in this case we set record to 'user', so each `<user>` above will be converted into a User model.
*
* Note that XmlReader doesn't care whether your {@link #root} and {@link #record} elements are nested deep inside a
* larger structure, so a response like this will still work:
*
* <?xml version="1.0" encoding="UTF-8"?>
* <deeply>
* <nested>
* <xml>
* <users>
* <user>
* <id>1</id>
* <name>Ed Spencer</name>
* <email>ed@sencha.com</email>
* </user>
* <user>
* <id>2</id>
* <name>Abe Elias</name>
* <email>abe@sencha.com</email>
* </user>
* </users>
* </xml>
* </nested>
* </deeply>
*
* # Response metadata
*
* The server can return additional data in its response, such as the {@link #totalProperty total number of records} and
* the {@link #successProperty success status of the response}. These are typically included in the XML response like
* this:
*
* <?xml version="1.0" encoding="UTF-8"?>
* <users>
* <total>100</total>
* <success>true</success>
* <user>
* <id>1</id>
* <name>Ed Spencer</name>
* <email>ed@sencha.com</email>
* </user>
* <user>
* <id>2</id>
* <name>Abe Elias</name>
* <email>abe@sencha.com</email>
* </user>
* </users>
*
* If these properties are present in the XML response they can be parsed out by the XmlReader and used by the Store
* that loaded it. We can set up the names of these properties by specifying a final pair of configuration options:
*
* reader: {
* type: 'xml',
* root: 'users',
* totalProperty : 'total',
* successProperty: 'success'
* }
*
* These final options are not necessary to make the Reader work, but can be useful when the server needs to report an
* error or if it needs to indicate that there is a lot of data available of which only a subset is currently being
* returned.
*
* # Response format
*
* **Note:** in order for the browser to parse a returned XML document, the Content-Type header in the HTTP response
* must be set to "text/xml" or "application/xml". This is very important - the XmlReader will not work correctly
* otherwise.
*/
Ext.define('Ext.data.reader.Xml', {
extend: 'Ext.data.reader.Reader',
alternateClassName: 'Ext.data.XmlReader',
alias : 'reader.xml',
<span id='Ext-data-reader-Xml-cfg-record'> /**
</span> * @cfg {String} record (required)
* The DomQuery path to the repeated element which contains record information.
*/
<span id='Ext-data-reader-Xml-method-createAccessor'> /**
</span> * @private
* Creates a function to return some particular key of data from a response. The totalProperty and
* successProperty are treated as special cases for type casting, everything else is just a simple selector.
* @param {String} key
* @return {Function}
*/
createAccessor: function(expr) {
var me = this;
if (Ext.isEmpty(expr)) {
return Ext.emptyFn;
}
if (Ext.isFunction(expr)) {
return expr;
}
return function(root) {
return me.getNodeValue(Ext.DomQuery.selectNode(expr, root));
};
},
getNodeValue: function(node) {
if (node && node.firstChild) {
return node.firstChild.nodeValue;
}
return undefined;
},
//inherit docs
getResponseData: function(response) {
var xml = response.responseXML,
error,
msg;
if (!xml) {
msg = 'XML data not found in the response';
error = new Ext.data.ResultSet({
total : 0,
count : 0,
records: [],
success: false,
message: msg
});
this.fireEvent('exception', this, response, error);
Ext.Logger.warn(msg);
return error;
}
return this.readRecords(xml);
},
<span id='Ext-data-reader-Xml-method-getData'> /**
</span> * Normalizes the data object.
* @param {Object} data The raw data object
* @return {Object} The documentElement property of the data object if present, or the same object if not.
*/
getData: function(data) {
return data.documentElement || data;
},
<span id='Ext-data-reader-Xml-method-getRoot'> /**
</span> * @private
* Given an XML object, returns the Element that represents the root as configured by the Reader's meta data.
* @param {Object} data The XML data object
* @return {XMLElement} The root node element
*/
getRoot: function(data) {
var nodeName = data.nodeName,
root = this.root;
if (!root || (nodeName && nodeName == root)) {
return data;
} else if (Ext.DomQuery.isXml(data)) {
// This fix ensures we have XML data
// Related to TreeStore calling getRoot with the root node, which isn't XML
// Probably should be resolved in TreeStore at some point
return Ext.DomQuery.selectNode(root, data);
}
},
<span id='Ext-data-reader-Xml-method-extractData'> /**
</span> * @private
* We're just preparing the data for the superclass by pulling out the record nodes we want.
* @param {XMLElement} root The XML root node
* @return {Ext.data.Model[]} The records
*/
extractData: function(root) {
var recordName = this.record;
//<debug>
if (!recordName) {
Ext.Error.raise('Record is a required parameter');
}
//</debug>
if (recordName != root.nodeName) {
root = Ext.DomQuery.select(recordName, root);
} else {
root = [root];
}
return this.callParent([root]);
},
<span id='Ext-data-reader-Xml-method-getAssociatedDataRoot'> /**
</span> * @private
* See Ext.data.reader.Reader's getAssociatedDataRoot docs.
* @param {Object} data The raw data object
* @param {String} associationName The name of the association to get data for (uses associationKey if present)
* @return {XMLElement} The root
*/
getAssociatedDataRoot: function(data, associationName) {
return Ext.DomQuery.select(associationName, data)[0];
},
<span id='Ext-data-reader-Xml-method-readRecords'> /**
</span> * Parses an XML document and returns a ResultSet containing the model instances.
* @param {Object} doc Parsed XML document
* @return {Ext.data.ResultSet} The parsed result set
*/
readRecords: function(doc) {
// it's possible that we get passed an array here by associations.
// Make sure we strip that out (see Ext.data.reader.Reader#readAssociated)
if (Ext.isArray(doc)) {
doc = doc[0];
}
<span id='Ext-data-reader-Xml-property-xmlData'> /**
</span> * @property {Object} xmlData
* Copy of {@link #rawData}.
* @deprecated Will be removed in Ext JS 5.0. Use {@link #rawData} instead.
*/
this.xmlData = doc;
return this.callParent([doc]);
},
<span id='Ext-data-reader-Xml-method-createFieldAccessExpression'> /**
</span> * @private
* Returns an accessor expression for the passed Field from an XML element using either the Field's mapping, or
* its ordinal position in the fields collsction as the index.
* This is used by buildExtractors to create optimized on extractor function which converts raw data into model instances.
*/
createFieldAccessExpression: function(field, fieldVarName, dataName) {
var selector = field.mapping || field.name,
result;
if (typeof selector === 'function') {
result = fieldVarName + '.mapping(' + dataName + ', this)';
} else {
result = 'me.getNodeValue(Ext.DomQuery.selectNode("' + selector + '", ' + dataName + '))';
}
return result;
}
});
</pre>
</body>
</html>