XML::XQL(3pm)
โจ NAME โจ
XML::XQL โ a perl module for querying XML tree structures with XQL ๐
๐ธ SYNOPSIS ๐ธ
okay bestie, here's the whole vibe in code:
use XML::XQL;
use XML::XQL::DOM;
$parser = new XML::DOM::Parser;
$doc = $parser->parsefile ("file.xml");
# Return all elements with tagName='title' under the root element 'book'
$query = new XML::XQL::Query (Expr => "book/title");
@result = $query->solve ($doc);
$query->dispose; # Avoid memory leaks - Remove circular references
# Or (to save some typing)
@result = XML::XQL::solve ("book/title", $doc);
# Or (to save even more typing)
@result = $doc->xql ("book/title");
three whole ways to do the thing, from extra to extra extra to literally one method call. pick ur fighter ๐
๐ฆ DESCRIPTION ๐ฆ
so XML::XQL implements the XQL (XML Query Language) proposal that got submitted to the XSL Working Group back in September 1998. the spec lives at http://www.w3.org/TandS/QL/QL98/pp/xql.html and honestly most of what you need to know about the XQL syntax is also chilling in the XML::XQL::Tutorial that ships with this distribution ๐
important!! XQL is not the same as XML-QL. do not conflate them, bestie, i am begging ๐ญ
rn this implementation only works with the XML::DOM module, but once the design settles down and the major bugs get flushed out, other extensions might follow โ like, for example, for XML::Grove. it's giving "coming soon, maybe" โจ
XQL was designed to be extensible and this implementation tries to stay true to that ๐ซถ users can add their own functions, methods, comparison operators and data types. plugging in a whole new XML tree structure (like XML::Grove) should be a piece of cake ๐ฐ
to use the XQL module, either do this:
use XML::XQL;
or this:
use XML::XQL::Strict;
the Strict module only gives you the core XQL functionality as found in the XQL spec. but by default โ meaning by using XML::XQL โ you get 'XQL+', which has some additional features on top ๐
see the section "Additional Features in XQL+" for exactly what those differences are ๐
this module is still in development ๐ง โ check the To-do list in XQL.pm for what still needs doing. any suggestions are welcome, fr fr: the sooner these implementation issues get resolved, the faster we can all use this module ๐
if you find a bug, you'd be doing me a HUGE favor by sending it to me as a test case ๐ see the file t/xql_template.t that comes with this distribution.
and if you've written a cool comparison operator, function, method or XQL data type that you wanna share, send it to tjmather@tjmather.com and i will add it to this module ๐ obsessed.
โจ XML::XQL global functions โจ
okay bestie, buckle up, this is the โจfunctionsโจ drawer โ everything here is a global level situation, meaning it's shared by all the queries you make from now on. no cap ๐ซถ
solve (QUERY_STRING, INPUT_LIST...)
the lil shortcut bestie. you call it like:
@result = XML::XQL::solve ("doc//book", $doc);
and it's literally just a chill way of writing this whole thing:
$query = new XML::XQL::Query (Expr => "doc//book");
@result = $query->solve ($doc);
$query->dispose;
and ngl, if you're rocking XML::XQL::DOM, you can also just write (peep XML::DOM::Node for the deets):
@result = $doc->xql ("doc//book");
setDocParser (PARSER)
sets the XML::DOM::Parser that the new XQL+ document() method uses. by default it's an XML::DOM::Parser made with zero arguments, i.e.
$PARSER = new XML::DOM::Parser;
defineFunction (NAME, FUNCREF, ARGCOUNT [, ALLOWED_OUTSIDE [, CONST, [QUERY_ARG]]])
defines an XQL function at the global level (so, for all newly created queries, slay) with the NAME you give it.
ARGCOUNTโ can be a single number or a reference to a list of numbers. a single number expands to[ARGCOUNT, ARGCOUNT]. the list holds pairs of numbers showing how many args the function allows.-1means infinity ๐. e.g.[2, 5, 7, 9, 12, -1]means the function can have 2, 3, 4, 5, 7, 8, 9, 12 or more arguments. the count gets checked when the XQL query string is parsed, so it will absolutely catch you ๐.- second parameter โ must be a reference to a Perl function or an anonymous sub. e.g.
'\&my_func'or'sub { ... code ... }' ALLOWED_OUTSIDE(default0) โ if you set it to1, the function or method may also be used outside subqueries in node queries. (see theNodeQueryparameter in the Query constructor)CONST(default0) โ if you set it to1, the function counts as "constant". see "Constant Function Invocations" for the whole story.QUERY_ARG(default0) โ if it's not-1, the argument at that index is treated as a 'query parameter'. if that query parameter is a subquery returning multiple values, the result list of the function invocation will hold one result value per subquery value. e.g.'length(book/author)'returns a list of Numbers โ the string lengths of all the author elements that'book/author'returned. cute, right? ๐
โ ๏ธ heads up: only methods (not functions) may show up after a Bang "!" operator. this gets checked when the XQL query string is parsed. no sneaky business.
see also: defineMethod
generateFunction (NAME, FUNCNAME, RETURN_TYPE [, ARGCOUNT [, ALLOWED_OUTSIDE [, CONST [, QUERY_ARG]]]])
generates and defines an XQL function wrapper for the Perl function named FUNCNAME. inside XQL query expressions the function will be called NAME.
- the return type should be one of the builtin XQL Data Types, or a class derived from XML::XQL::PrimitiveType (see "Adding Data Types").
- for what
ARGCOUNT,ALLOWED_OUTSIDE,CONSTandQUERY_ARGmean, go peek atdefineFunction.
the way function values work: they're always converted to Perl strings with xql_toString before being handed to the Perl function implementation. the return value gets cast to an object of type RETURN_TYPE, or to the empty list [] if the result is undef. it uses expandType to expand XQL primitive type names. and if RETURN_TYPE is "*", it returns the function result exactly as-is โ unless the result is undef, in which case you get []. periodt ๐
defineMethod (NAME, FUNCREF, ARGCOUNT [, ALLOWED_OUTSIDE])
defines an XQL method at the global level (all newly created queries, bestie) with the NAME you specify.
ARGCOUNTโ single number or a reference to a list of numbers. a single number expands to[ARGCOUNT, ARGCOUNT]. the list has pairs of numbers showing how many arguments the method allows.-1means infinity ๐. e.g.[2, 5, 7, 9, 12, -1]means the method can take 2, 3, 4, 5, 7, 8, 9, 12 or more arguments. count gets checked at XQL query string parse time.- second parameter โ must be a reference to a Perl function or an anonymous sub. e.g.
'\&my_func'or'sub { ... code ... }' ALLOWED_OUTSIDE(default0) โ set it to1and the function or method can also be used outside subqueries in node queries. (see theNodeQueryparameter in the Query constructor)
โ ๏ธ again for the people in the back: only methods (not functions) may appear after a Bang "!" operator. checked when parsing the XQL query string.
see also: defineFunction
defineComparisonOperators (NAME => FUNCREF [, NAME => FUNCREF]*)
defines XQL comparison operators at the global level โจ. each FUNCREF must be a reference to a Perl function or an anonymous sub. e.g. '\&my_func' or 'sub { ... code ... }'
for example, defining the operators $my_op$ and $my_op2$:
defineComparisonOperators ('my_op' => \&my_op,
'my_op2' => sub { ... insert code here ... });
defineElementValueConvertor (TAG_NAME, FUNCREF)
this one says: the result of the value() call for Elements with the given TAG_NAME should use the specified function ๐ฆ.
the function receives two parameters. the second one is the TAG_NAME of the Element node, and the first one is the Element node itself. FUNCREF should be a reference to a Perl function (like \&my_sub) or an anonymous sub.
e.g. to make every Element with tag name 'date-of-birth' return XML::XQL::Date objects:
defineElementValueConvertor ('date-of-birth', sub {
my $elem = shift;
# Always pass in the node as the second parameter. This is
# the reference node for the object, which is used when
# sorting values in document order.
new XML::XQL::Date ($elem->xql_text, $elem);
});
these converters can only be specified at a global level โ not on a per query basis. to undefine a converter, just pass aFUNCREFofundef. bye bye converter ๐
defineAttrValueConvertor (ELEM_TAG_NAME, ATTR_NAME, FUNCREF)
same energy, but for Attributes: it says the result of the value() call for Attributes with the given ATTR_NAME whose parent Element has the given ELEM_TAG_NAME should use the specified function.
- an
ELEM_TAG_NAMEof"*"matches no matter what the parent Element's tag name is ๐ - the function receives 3 parameters. the third is the tag name of the parent Element (yes, even if
ELEM_TAG_NAMEwas"*"), the second is theATTR_NAME, and the first is the Attribute node itself.FUNCREFshould be a reference to a Perl function (like\&my_sub) or an anonymous sub.
same rule as above: converters can only be specified globally, not per query. to undefine one, pass aFUNCREFofundef. ๐
defineTokenQ (Q)
defines the token for the q// string delimiters at a global level. the default value for XQL+ is 'q', and for XML::XQL::Strict it's undef. a value of undef will deactivate this feature entirely.
defineTokenQQ (QQ)
defines the token for the qq// string delimiters at a global level. default for XQL+ is 'qq', for XML::XQL::Strict it's undef. a value of undef will deactivate the feature.
expandType (TYPE)
used internally to expand type names of XQL primitive types. e.g. it expands "Number" into "XML::XQL::Number", and it's not case-sensitive โ so "number" and "NuMbEr" both expand correctly. iconic. ๐
defineExpandedTypes (ALIAS, FULL_NAME [, ...])
for each pair of arguments, it lets the class name FULL_NAME be abbreviated with ALIAS. these definitions get used by expandType().
(ALIASis always converted to lowercase internally, becauseexpandTypeis case-insensitive.)
also, lowkey important: overriding the ALIAS for "date" also affects the object type returned by the date() function. so like, don't be surprised. ๐
setErrorContextDelimiters (START, END, BOLD_ON, BOLD_OFF)
sets the delimiters used when printing error messages during query evaluation ๐.
- the default delimiters on Unix are
`tput smul`(underline on) and`tput rmal`(underline off). on other systems (the ones withouttput), the delimiters are">>"and"<<"respectively. - when printing the error message, the subexpression that caused the error gets wrapped in the delimiters โ i.e. underlined on Unix โจ
- for certain subexpressions, the significant keyword (e.g.
"$and$") gets wrapped in the bold delimitersBOLD_ON(default:`tput bold`on Unix,""elsewhere) andBOLD_OFF(default: (`tput rmul`.`tput smul`) on Unix,""elsewhere โ see$BoldOffin XML::XQL::XQL.pm for the details).
isEmptyList (VAR)
returns 1 if VAR is [], otherwise 0. super handy inside user defined functions ๐ซถ
๐ธ Additional Features in XQL+ ๐ธ
Parent operator '..'
the '..' operator returns the parent of the current node โ whereas '.' would return the current node itself. this is not part of any XQL standard, because normally you'd use return operators, and those aren't implemented here. iykyk ๐
Sequence operators ';' and ';;'
the sequence operators ';' (precedes) and ';;' (immediately precedes) are not in the XQL spec, but they are described in 'The Design of XQL' by Jonathan Robie, who is one of the designers of XQL. you can find it at http://www.texcel.no/whitepapers/xql-design.html. also peep the XQL Tutorial for a description of what they actually mean ๐ฆ
q// and qq// String Tokens
string tokens ร la q// and qq// are allowed โจ. q// evaluates like Perl's single quotes and qq// like Perl's double quotes.
โ ๏ธ note that the default XQL strings don't allow escaping etc., so it's not possible to define a string containing both single and double quotes. ๐ญ
if 'q' and 'qq' aren't your vibe, you may redefine them to something else, or undefine them altogether by assigning undef to them. e.g.:
# at a global level - shared by all queries (that don't (re)define 'q')
XML::XQL::defineTokenQ ('k');
XML::XQL::defineTokenQQ (undef);
# at a query level - only defined for this query
$query = new XML::XQL::Query (Expr => "book/title", q => 'k', qq => undef);
from now on k// works like q// did, and qq// doesn't work at all anymore. glow up complete ๐
โจ
Query strings can have embedded Comments
for example:
$queryExpr = "book/title # this comment is inside the query string
[. = 'Moby Dick']"; # this comment is outside
Optional dollar delimiters and case-insensitive XQL keywords
the following XQL keywords are case-insensitive and the dollar sign delimiters may be omitted: $and$, $or$, $not$, $union$, $intersect$, $to$, $any$, $all$, $eq$, $ne$, $lt$, $gt$, $ge$, $le$, $ieq$, $ine$, $ilt$, $igt$, $ige$, $ile$. slay ๐
- E.g.
$AND$,$And$,$aNd$,and,And,aNdare all valid replacements for$and$. so yes bestie, you can lowercase-ify your operator names however your heart desires ๐ - BUT โ heads up โ the XQL+ comparison operators (
$match$,$no_match$,$isa$,$can$) still need their dollar delimiters and they are case-sensitive. no lowercase games for those ones, periodt. - Comparison operator:
$match$or'=~'โ E.g."book/title =~ '/(Moby|Dick)/']"will return allbooktitles containing Moby or Dick. Note that the match expression needs to be quoted and should contain the//orm//delimiters for Perl. When casting the values to be matched, both are converted toText. ๐ฆ - Comparison operator:
$no_match$or'!~'โ E.g."book/title !~ '/(Moby|Dick)/']"will return allbooktitles that don't contain Moby or Dick. Again, the match expression needs to be quoted and should contain the//orm//delimiters for Perl. And again, when casting the values to be matched, both are converted toText. ๐ - Comparison operator:
$isa$โ E.g.'//. $isa$ "XML::XQL::Date"'returns all elements for which thevalue()function returns anXML::XQL::Dateobject. (Sneaky detail: thevalue()function can be overridden to return a specific object type for certain elements and attributes.) It usesexpandTypeto expand XQL primitive type names. ๐ - Comparison operator:
$can$โ E.g.'//. $can$ "swim"'returns all elements for which thevalue()function returns an object that implements the (Perl)swim()method. (Same deal again: thevalue()function can be overridden to return a specific object type for certain elements and attributes.) ๐
- Function:
once (QUERY)โ E.g.'once(id("foo"))'will evaluate theQUERYexpression only once per query. Certain query results (like the example above) will always return the same value within a query, andonce()caches theQUERYresult for the rest of the query. lowkey a glow-up for your performance โจ
Also note: "constant" function invocations are always cached โ see also "Constant Function Invocations".
- Function:
subst (QUERY, EXPR, EXPR [,MODIFIERS, [MODE]])โ E.g.'subst(book/title, "[M|m]oby", "Dick", "g")'will replace Moby or moby with Dick globally ("g") in allbooktitleelements. Underneath it uses Perl's substitute operators///. Don't worry about which delimiters are used underneath, bestie, it's handled. The function returns all thebook/titles for which a substitution actually occurred. The defaultMODIFIERSstring is""(empty.) And the function name may be abbreviated to"s".
For most Node types, it converts the value() to a string (with xql_toString) to match the string and xql_setValue to set the new value in case it matched. For XQL primitives (Boolean, Number, Text) and other data types (e.g. Date) it uses xql_toString to match the String and xql_setValue to set the result.
โ ๏ธ Beware: performing a substitution on a primitive that was found in the original XQL query expression changes the value of that constant. like, it rewrites the source, bestie. no cap. ๐ญ
If MODE is 0 (default), it treats Element nodes differently by matching and replacing text blocks occurring in the Element node. A text block is defined as the concatenation of the raw text of subsequent Text, CDATASection and EntityReference nodes. In this mode it skips embedded Element nodes. If a text block matches, it is replaced by a single Text node, regardless of the original node type(s).
If MODE is 1, it treats Element nodes like the other nodes, i.e. it converts the value() to a string etc. Note that the default implementation of value() calls text(), which normalizes whitespace and includes embedded Element descendants (recursively.) This is probably not what you want to use in most cases, but since I'm not a professional psychic... :-) ๐
- Function:
map (QUERY, CODE)โ E.g.'map(book/title, "s/[M|m]oby/Dick/g; $_")'will replace Moby or moby with Dick globally ("g") in allbooktitleelements. Underneath it uses Perl'smapoperator. The function returns all thebook/titles for which a change occurred. ๐ง
??? add more specifics
- Function:
eval (EXPR [,TYPE])โ Evaluates the Perl expressionEXPRand returns an object of the specifiedTYPE. It usesexpandTypeto expand XQL primitive type names. If the result of theevalwasundef, the empty list[]is returned. ๐ฆ
E.g. 'eval("2 + 5", "Number")' returns a Number object with the value 7, and
'eval("%ENV{USER}")'
returns a Text object with the user name.
Consider using once() to cache the return value, when the invocation will return the same result for each invocation within a query.
??? add more specifics
- Function:
new (TYPE [, QUERY [, PAR] *])โ Creates a new object of the specified objectTYPE. The constructor may have any number of arguments. The first argument of the constructor (the 2nd argument of thenew()function) is considered to be a 'query parameter'. SeedefineFunctionfor a definition of query parameter. It usesexpandTypeto expand XQL primitive type names. โจ - Function:
document (QUERY)ordoc (QUERY)โ Thedocument()function creates a newXML::XML::Documentfor each result ofQUERY(QUERYmay be a simple string expression, like"/usr/enno/file.xml". Seet/xql_document.tor below for an example with a more complexQUERY.)
document() may be abbreviated to doc().
document() uses an XML::DOM::Parser underneath, which can be set with XML::XQL::setDocParser(). By default it uses a parser that was created without any arguments, i.e.
$PARSER = new XML::DOM::Parser;
Let's try a more complex example, assuming $doc contains:
<doc>
<file name="file1.xml"/>
<file name="file2.xml"/>
</doc>
Then the following query will return two XML::XML::Documents, one for file1.xml and one for file2.xml:
@result = XML::XQL::solve ("document(doc/file/@name)", $doc);
The resulting documents can be used as input for following queries, e.g.
@result = XML::XQL::solve ("document(doc/file/@name)/root/bla", $doc);
will return all /root/bla elements from the documents returned by document(). ๐ฅ
- Method:
DOM_nodeType ()โ Returns the DOM node type. Note that these are mostly the same asnodeType(), except forCDATASectionandEntityReferencenodes.DOM_nodeType()returns4and5respectively, whereasnodeType()returns3, because they are considered text nodes. ๐
โจ Function wrappers for Perl builtin functions โจ
XQL function wrappers have been provided for most Perl builtin functions. When using a Perl builtin function like "substr" in an XQL+ querry, an XQL function wrapper will be generated on the fly. The arguments to these functions may be regular XQL+ subqueries (that return one or more values) for a query parameter (see generateFunction for a definition.) Most wrappers of Perl builtin functions have argument 0 for a query parameter, except for: chmod (parameter 1 is the query parameter), chown (2) and utime (2). The following functions have no query parameter, which means that all parameters should be a single value: atan2, rand, srand, sprintf, rename, unlink, system.
The function result is casted to the appropriate XQL primitive type (Number, Text or Boolean), or to an empty list if the result was undef. ๐
โจ XPath functions and methods โจ
The following functions were found in the XPath specification: ๐
- Function:
concat (STRING, STRING, STRING*)โ Theconcatfunction returns the concatenation of its arguments. ๐ซถ - Function:
starts-with (STRING, STRING)โ Thestarts-withfunction returnstrueif the first argument string starts with the second argument string, and otherwise returnsfalse. - Function:
contains (STRING, STRING)โ Thecontainsfunction returnstrueif the first argument string contains the second argument string, and otherwise returnsfalse. - Function:
substring-before (STRING, STRING)โ Thesubstring-beforefunction returns the substring of the first argument string that precedes the first occurrence of the second argument string in the first argument string, or the empty string if the first argument string does not contain the second argument string. For example,
substring-before("1999/04/01","/") returns 1999.
๐ธ
- Function:
substring-after (STRING, STRING)โ Thesubstring-afterfunction returns the substring of the first argument string that follows the first occurrence of the second argument string in the first argument string, or the empty string if the first argument string does not contain the second argument string. For example,
substring-after("1999/04/01","/") returns 04/01,
and
substring-after("1999/04/01","19") returns 99/04/01.
๐ฆ
- Function:
substring (STRING, NUMBER [, NUMBER] )โ Thesubstringfunction returns the substring of the first argument starting at the position specified in the second argument with length specified in the third argument. For example,
substring("12345",2,3) returns "234".
If the third argument is not specified, it returns the substring starting at the position specified in the second argument and continuing to the end of the string. For example,
substring("12345",2) returns "2345".
More precisely, each character in the string is considered to have a numeric position: the position of the first character is 1, the position of the second character is 2 and so on.
โ ๏ธ NOTE: This differs from thesubstrmethod, in which the method treats the position of the first character as0. so like, off-by-one central, bestie. ๐
The XPath spec says this about rounding, but that is not true in this implementation: The returned substring contains those characters for which the position of the character is greater than or equal to the rounded value of the second argument and, if the third argument is specified, less than the sum of the rounded value of the second argument and the rounded value of the third argument; the comparisons and addition used for the above follow the standard IEEE 754 rules; rounding is done as if by a call to the round function. ๐
- Method:
string-length ( [ QUERY ] )โ Thestring-lengthreturns the number of characters in the string. If the argument is omitted, it defaults to the context node converted to a string, in other words the string-value of the context node.
Note that the generated XQL wrapper for the Perl built-in substr does not allow the argument to be omitted. ๐ญ
Method: normalize-space ( [ QUERY ] )
Okay so normalize-space is the glow-up function โจ it takes a string and returns it with the whitespace normalized โ meaning it strips the leading and trailing whitespace, and squishes every run of whitespace characters down into a single space. What counts as a whitespace character? The same ones the S production in XML allows, no more no less ๐
And if you don't pass an argument at all? It defaults to the context node converted to a string โ in other words, the string-value of the context node. Comfy default, we love to see it ๐ซถ
Function: translate (STRING, STRING, STRING)
Bestie, translate is the character-swap function ๐ It returns the first argument string with occurrences of characters in the second argument string replaced by the character sitting at the corresponding position in the third argument string. Like, for example:
translate("bar","abc","ABC") returns the string BAr.
Iconic. But here's the plot twist ๐ญ if there's a character in the second argument string with no character at a corresponding position in the third argument string (because the second argument string is longer than the third), then occurrences of that character in the first argument string just get removed. Straight up deleted. For example:
translate("--aaa--","abc-","ABC") returns "AAA".
And a couple more rules so you don't get caught lacking:
- If a character occurs more than once in the second argument string, then the first occurrence determines the replacement character.
- If the third argument string is longer than the second argument string, then excess characters are ignored.
NOTE: the translate function is not a sufficient solution for case conversion in all languages. A future version may provide additional functions for case conversion. So don't come for it ๐ธ
Fun implementation lore: this function was implemented using tr///d. Periodt ๐
Function: sum ( QUERY )
sum does exactly what it says on the tin ๐งฎ it returns the sum of the QUERY results, by converting the string values of each result to a number. Addy addy add ๐ฅ
Function: floor (NUMBER)
floor returns the largest number (closest to positive infinity) that is not greater than the argument and that is an integer. So it rounds down-ish, technically toward negative infinity, but who's counting ๐
Function: ceiling (NUMBER)
ceiling returns the smallest number (closest to negative infinity) that is not less than the argument and that is an integer. That's the up-and-over one ๐ฆ
Function: round (NUMBER)
round returns the number that is closest to the argument and that is an integer. And if there are two such numbers (the classic .5 situation, we've all been there ๐), then the one that is closest to positive infinity is returned. No cap โจ
๐ Implementation Details ๐
XQL Builtin Data Types
So the XQL engine uses these object classes internally, and only Number, Boolean and Text are considered primitive XQL types ๐
XML::XQL::Numberโ for integers and floating point numbers.XML::XQL::Booleanโ for booleans, e.g. returned bytrue()andfalse().XML::XQL::Textโ for string values.XML::XQL::Dateโ for date, time and date/time values. E.g. returned by thedate()function.XML::XQL::Nodeโ superclass of all XML node types. E.g. all subclasses ofXML::DOM::Nodesubclass from this.- Perl list reference โ lists of values are passed by reference (i.e. using
[]delimiters). The empty list[]has a double meaning. It also means 'undef' in certain situations, e.g. when a function invocation or comparison failed. ๐ญ
Type casting in comparisons
When two values are compared in an XML comparison (e.g. $eq$) the values are first casted to the same data type. Node values are first replaced by their value() (i.e. the XQL value() function is used, which returns a Text value by default, but may return any data type if the user so chooses.) Then the resulting values are casted to the type of the object with the highest xql_primType() value. Here's the ranking, from lowest to highest ๐
Node (0), Text (1), Number (2), Boolean (3), Date (4), other data types (4 by default, but this may be overridden by the user.)
E.g. if one value is a Text value and the other is a Number, the Text value is cast to a Number and the resulting low-level (Perl) comparison is (for $eq$):
$number->xql_toString == $text->xql_toString
If both were Text values, it would have been:
$text1->xql_toString eq $text2->xql_toString
Real talk: the XQL spec is vague and even conflicting where it concerns type casting. This implementation resulted after talking to Joe Lapp, one of the spec writers. So like, we did our homework ๐ซถ
Adding Data Types
Wanna add your own data type? Slay ๐
just make sure it derives from XML::XQL::PrimitiveType and implements the necessary methods.
The author says more stuff will be added here to explain it all, but for now, look at the code for the primitive XQL types or the Date class (XML::XQL::Date in Date.pm.) iykyk ๐
Document Order
The XQL spec states that query results always return their values in document order, which means the order in which they appeared in the original XML document. Values extracted from Nodes (e.g. with value(), text(), rawText(), nodeName(), etc.) always have a pointer to the reference node (i.e. the Node from which the value was extracted.) These pointers are acknowledged when (intermediate) result lists are sorted. Currently, the only place where a result list is sorted is in a $union$ expression, which is the only place where the result list can be unordered. (If you find that this is not true, let me know.) ๐
Non-node values that have no associated reference node always end up at the end of the result list in the order that they were added. The XQL spec states that the reference node for an XML Attribute is the Element to which it belongs, and that the order of values with the same reference node is undefined. This means that the order of an Element and its attributes would be undefined. But since the XML::DOM module keeps track of the order of the attributes, the XQL engine does the same, and therefore, the attributes of an Element are sorted and appear after their parent Element in a sorted result list. Iconic behaviour honestly โจ
Constant Function Invocations
If a function always returns the same value when given "constant" arguments, the function is considered to be "constant". A "constant" argument can be either an XQL primitive (Number, Boolean, Text) or a "constant" function invocation. E.g.
date("12-03-1998")
true()
sin(0.3)
length("abc")
date(substr("12-03-1998 is the date", 0, 10))
are constant, but not:
length(book[2])
Results of constant function invocations are cached and calculated only once for each query. See also the CONST parameter in defineFunction. It is not necessary to wrap constant function invocations in a once() call. So don't do extra work, bestie, the engine already ate ๐ฅ
Constant XQL functions are: date, true, false and a lot of the XQL+ wrappers for Perl builtin functions. Function wrappers for certain builtins are not made constant on purpose to force the invocation to be evaluated every time, e.g. 'mkdir("/user/enno/my_dir", "0644")' (although constant in appearance) may return different results for multiple invocations. See %PerlFunc in Plus.pm for details.
Function: count ([QUERY])
Okay so the count() function has no parameters in the XQL spec. But in this implementation it will return the number of QUERY results when passed a QUERY parameter. Little bonus feature, we love her ๐ซถ
Method: text ([RECURSE])
When expanding an Element node, the text() method adds the expanded text() value of sub-Elements. When RECURSE is set to 0 (default is 1), it will not include sub-elements. This is useful e.g. when using the $match$ operator in a recursive context (using the // operator), so it won't return parent Elements when one of the children matches. Very demure, very mindful ๐
Method: rawText ([RECURSE])
See text(). Yeah, that's the whole thing. Go look at text() bestie ๐ญ
๐ SEE ALSO ๐
XML::XQL::Query,XML::XQL::DOM,XML::XQL::Date- The Japanese version of this document can be found on-line at
http://member.nifty.ne.jp/hippo2000/perltips/xml/xql.htm - The
XML::XQL::Tutorialmanual page. The Japanese version can be found athttp://member.nifty.ne.jp/hippo2000/perltips/xml/xql/tutorial.htm - The XQL spec at
http://www.w3.org/TandS/QL/QL98/pp/xql.html - The Design of XQL at
http://www.texcel.no/whitepapers/xql-design.html - The DOM Level 1 specification at
http://www.w3.org/TR/REC-DOM-Level-1 - The XML spec (Extensible Markup Language 1.0) at
http://www.w3.org/TR/REC-xml - The
XML::ParserandXML::Parser::Expatmanual pages.
๐ AUTHOR ๐
Enno Derksen is the original author. Legend behaviour, truly ๐ซถ
Please send bugs, comments and suggestions to T.J. Mather <tjmather@tjmather.com> โ he's literally waiting for your emails, don't be shy ๐ฌ
and that's the whole page, bestie!! ๐๐โจ This one was generated by perl v5.36.0 on 2022-12-06, filed under XML::XQL(3pm) โ so if you ever need to cite your source, that's the receipt ๐ go forth and query responsibly, mwah ๐
๐
๐ฆ where this page comes from
- debian bookworm: libxml-xql-perl 0.68-9 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libxml-xql-perl 0.68-9 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libxml-xql-perl 0.68-9 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libxml-xql-perl 0.68-9 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libxml-xql-perl 0.68-9 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms