📄 xmltools.js
字号:
element.setAttribute(propName, values[propName]); }},// XPath // ---------------------------------------------------------------------------------------// make all namespaces declared on the document element available under the same prefix used in// the document itself, and make the default namespace, if there is one, available as// "default". "namespaces" can be passed it to avoid redeclaration of existing prefixes._makeIEDefaultNamespaces : function (doc, namespaces) { var buffer = isc.SB.create(), docElement = doc.documentElement, namespaces = namespaces || isc.emptyObject, defaultNamespace; // if it hasn't been specified explicitly, try to figure out the default namespaces // NOTE: default is a keyword in IE if (!namespaces["default"]) { defaultNamespace = this._deriveDefaultNamespace(docElement); if (defaultNamespace) buffer.append('xmlns:default="', defaultNamespace, '" '); } // add all the namespaces on the document element var attrs = doc.documentElement.attributes; for (var i = 0; i < attrs.length; i++) { var attr = attrs[i], prefix = attr.prefix; // NOTE: attr.name is the full name of the attribute, including prefix, so for a // non-default namespace declaration attr.name will be eg xmlns:somePrefix if (prefix == "xmlns" && prefix != attr.name) { // don't redefine already defined selection namespaces // NOTE: baseName is IE-only if (namespaces[attr.baseName] != null) continue; buffer.append(attr.name, '="', attr.value, '" '); } } return buffer.toString();},// Method to determine the "default" namespace by looking at the namespaceURI of the document// element or (if appropriate) children of the documentElement.// Used in IE as part of _makeIEDefaultNamespaces()// // Note: This handles the common case where the document element declares a namespace, but // is itself in a different namespace_deriveDefaultNamespace : function (docElement) { var shouldLog = this.logIsDebugEnabled("xmlSelect"); if ((docElement.prefix == null || isc.isAn.emptyString(docElement.prefix)) && docElement.namespaceURI) { if (shouldLog) { this.logWarn("using docElement ns, prefix: " + docElement.prefix, "xmlSelect"); } return docElement.namespaceURI; } else if (docElement.firstChild) { var defaultNamespace for (var i = 0; i < docElement.childNodes.length; i++) { var childNode = docElement.childNodes[i]; // text nodes show up in the childNodes collection in Safari if (childNode.nodeType == 3) continue; var nsURI = childNode.namespaceURI; if (!nsURI) break; if (childNode.prefix == null || isc.isAn.emptyString(childNode.prefix)) { defaultNamespace = childNode.namespaceURI; break; } } if (defaultNamespace != null) { if (shouldLog) { this.logDebug("using default namespace detected on child: " + defaultNamespace, "xmlSelect"); } } // if there is no default namespace, still define the namespace prefix "default" as // the document element's namespace, for conveniece if (defaultNamespace == null && docElement.namespaceURI) { defaultNamespace = docElement.namespaceURI; if (shouldLog) { this.logDebug("using document element's namespace as default namespace: " + defaultNamespace, "xmlSelect"); } } // if no appropriate default namespace could be derived, still define one, so that // using "default:" doesn't create a JS error. This allows an XPath like // "//default:item|//item" to handle both namespaced and non-namespaced RSS feeds if (!defaultNamespace) defaultNamespace = "http://openuri.org/defaultNamespace"; return defaultNamespace; }},//> @classMethod xmlTools.selectObjects() (A)// Applies an XPath expression to JavaScript objects, returning matching objects.// <P>// Both child and attribute names are interpreted as property names, and array access notation// can be used to select elements from Arrays. For example:<pre>// var results = {// searchResults:[// { title:"Page One", relevance:6.3 },// { title:"Page Two", relevance:5.2, // summary: "Summary of Page One" }// ]// };//// // returns the "searchResults" two-item Array// isc.XMLTools.selectObjects(results, "/searchResults");//// // returns the first item under "searchResults", in an Array (NOTE: in XPath, Array// // index starts at 1, not 0)// isc.XMLTools.selectObjects(results, "/searchResults[1]");//// // returns ["Page One"]// isc.XMLTools.selectObjects(results, "/searchResults[1]/title");//// // also returns ["Page One"]// isc.XMLTools.selectObjects(results, "/searchResults[1]@title");// </pre>// A limited form of XPath "predicates", that is, expressions with brackets that filter// returned objects, is allowed. A predicate can be either:// <ul>// <li> a number only, eg [5], for Array access// <li> the XPath function call "last()", eg [last()], to retrieve the last item// <li> a property name (*without* any leading "@"), meaning that the property contains a value// that is considered "true" in JavaScript. For example: [summary]// <li> a property name, comparison operator, and either a number or String literal, for// example, [name = "bob"]. In this case the property can also be the XPath function// position(), for example, [position() > 5]// </ul>// Some examples of using simple predicates with the sample data above:// <pre>// // returns an Array with only the first result// isc.XMLTools.selectObjects(results, "/searchResults[relevance > 5.5]");// // // return an Array with only the second result, since the first has no summary// isc.XMLTools.selectObjects(results, "/searchResults[summary]");// </pre>// Details of the XPath -> Objects mapping:// <ul> // <li> JavaScript Object properties are considered element children, and text children do not// exist (in the XML model, text children exist *between* element children, but nothing// exists between JavaScript properties)// <li> The contents of Array-valued properties are considered immediate element children (this// is consistent with the predicate "[5]" acting like Array access)// <li> "*" in XML selects all element children, so "*" in Object XPath selects the values of// all properties, that is, +link{classMethod:isc.getValues(),isc.getValues(object)}, except// that Array-valued properties are "flattened" into the returned list.// </ul>//// @param object (Object) Object to select results from// @param xPath (String) XPath expression// @return (Array) Array of matching objects, or null for no match// @visibility external//<_$leftBracket : "[",selectObjects : function (object, xPath, singleValue) { if (isc.contains("|")) { var subExpressions = xPath.split(/|/), results = []; for (var i = 0; i < subExpressions.length; i++) { results.addList(this.selectObjects(subExpressions[i], object)); } return results; } // canonicalize to an Array var objects = isc.isAn.Array(object) ? object : [object]; if (xPath != isc.slash) { if (isc.startsWith(xPath, isc.slash)) xPath = xPath.substring(1); var segments = xPath.split(/[\/@]/); //this.logWarn("segments: " + this.echo(segments)); objects = this._selectObjects(segments, objects, isc.slash); } // return the single value, or null on no match if (singleValue && objects.length <= 1) return objects[0]; return objects;},_selectObjects : function (segments, objects, path) { var segment = segments[0]; segments = segments.length > 1 ? segments.slice(1) : null; //this.logWarn("at path: " + path + // ", applying segment: '" + segment + // "' to " + this.echoLeaf(objects)); if (objects == null) return null; // break segment into nodeTest and predicate var predicate, nodeTest = segment, bracketIndex = segment.indexOf(this._$leftBracket); if (bracketIndex != -1) { nodeTest = segment.substring(0, bracketIndex); // extract predicate expression (NOTE: assume one only) predicate = segment.substring(bracketIndex + 1, segment.length-1); //this.logWarn("nodeTest: " + nodeTest + ", predicate: " + predicate); } // apply nodeTest to each node var resultObjects = []; for (var i = 0; i < objects.length; i++) { var object = objects[i]; // apply the node test if (nodeTest != isc.star) { object = object[nodeTest]; } else { var properties = isc.getValues(object); object = []; for (var i = 0; i < properties.length; i++) { if (!isc.isAn.Array(properties[i])) object.add(properties[i]); else object.addList(properties[i]); } } //this.logWarn("nodeTest: " + nodeTest + ", result: " + this.echoLeaf(object)); if (object == null) continue; if (!isc.isAn.Array(object)) { resultObjects.add(object); } else { resultObjects.addList(object); } } // filter result by predicate if (predicate) { // canonicalize object to an Array if we have a predicate var predResult = this._applyPredicateExpression(resultObjects, predicate); //this.logWarn("predicate expression: '" + predicate + // "' applied to: " + this.echoLeaf(resultObjects) + // ", with result: " + this.echoLeaf(predResult)); resultObjects = predResult; } if (segments == null || segments.length == 0) return resultObjects; //this.logWarn("recursing with remaining path: " + segments.join("/")); // recurse if there are more segments path += segment + isc.slash; return this._selectObjects(segments, resultObjects, path);},_applyPredicateExpression : function (objects, expr) { // check for simple index (this will actually accept anything that starts with a // number) var index = parseInt(expr); if (!isNaN(index)) { // xPath indices are 1-based return [objects[index - 1]]; } if (expr == "last()") return [objects.last()]; // NOTE: not making property first char vs remaining chars distinction in XML "QName" // identifier. Allowing () as a quick way to allow the position() function var parts = expr.match(/^([a-zA-Z_0-9:\-\.\(\)]*)\s*(<|>|!=|=|<=|>=|)\s*(.*)$/), property, operator, value; //this.logWarn("predicate parts: " + parts); if (parts == null) { // assume just an identifier if (!expr.match(/^[a-zA-Z_0-9:\-\.]*$/)) { this.logWarn("couldn't parse predicate expression: " + expr); return null; } property = expr; } else { property = parts[1], // parts[0] is the entire match operator = parts[2], value = parts[3]; } // convert this simple expression to a JavaScript expression we can apply to each object // XPath uses single = operator if (operator == "=") operator = "=="; // XPath uses functions for true and false literals if (value == "true()") value = true; else if (value == "false()") value = false; // support the position() function specially, by passing in params if (property == "position()") property = "position"; //this.logWarn("property: " + property + ", operator: " + operator + ", value: " + value); var predFunc = new Function ("item,position", "return " + (property != "position" ? "item." : "") + property + (operator ? operator + value : "")); // apply the function to each object var matchingObjects = []; //this.logWarn("predicate function: " + predFunc); for (var i = 0; i < objects.length; i++) { if (predFunc(objects[i], i+1)) matchingObjects.add(objects[i]); } return matchingObjects;},//> @classMethod XMLTools.selectNodes()// Retrieve a set of nodes from an XML element or document based on an XPath expression.// <P>// If the target document is namespaced, namespace prefixes declared in the document element of// the target document will be available, as well as the default namespace, if declared, under// the prefix "default".// <P>// To declare your own namespace prefixes, provide a prefix to URI mapping as a simple JS// Object, for example:// <pre>// {// az : "http://webservices.amazon.com/AWSECommerceService/2005-03-23",// xsd : "http://www.w3.org/2001/XMLSchema"// }// </pre>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -