How to use make srai work for both cases in AIML? - aiml

How to use one AIML srai category in such a way that it will redirect to the category regardless of whether there is a word after the keyword or not?
For instance
<category>
<pattern>WHERE IS SOMEPLACE</pattern>
<template>here</template>
</category>
<category>
<pattern> * SOMEPLACE *</pattern>
<template>
<srai>WHERE IS SOMEPLACE</srai>
</template>
</category>
it works when I ask something like, "tell me where someplace is". But it doesnt work when I ask, "where is someplace". it works for case 2 when i remove the last * but then it doesn't work for case 1. I realize I can use two different categories but then I would be increasing categories beyond what I need so I can't do that.
Please forgive my english as it is not my native language.
Thank you.
edit: if it helps anyone, AIML 2.0 contains zero+ wildcards, which is essentially the same as _ or * but also accounts for zero words as well. The new order of precedence, (from highest to lowest) is # (zero+), _ , ^(zero+) and *.
Please checkout https://docs.google.com/document/d/1wNT25hJRyupcG51aO89UcQEiG-HkXRXusukADpFnDs4/pub for more information.

It's dangerous to use the same word in your srai as the keyword you are trying to match, as you will get an infinite recursion error. Try this instead, which will srai to the top category, as long as your input has the word "SOMEPLACE" in it:
<category>
<pattern>SomeplaceAnswer</pattern>
<template>here</template>
</category>
<category>
<pattern># SOMEPLACE #</pattern>
<template>
<srai>SomeplaceAnswer</srai>
</template>
</category>

Related

XSL-FO | FO: Tags are being removed

I'm currently using XSL for a work project and I'm facing an issue.
I'm trying to read values for a database that look like this:
<fo:block font-weight='bold>hello</fo:block>
and it seems that XSL is stripping the <fo:block> element because it gives me text only ( I only see Hello, not in bold, and it doesn't behave like a block element ). I feel like, somehow, that XSL interprets the value read from the DB as a string, and strip of the <fo> tags, leaving my with text only.
Any idea what could be done in order that my styling get preserved?
( Obviously this example have been simplified, the text to be displayed is longer than that )
EDIT : Self answered for future references
Based on the comments you want to change <xsl:value-of select='/fulfill-list/ticket-list/list-item/eventTicketConte‌​nt/xmlTicketContent/‌​ticketdescription'/> to <xsl:copy-of select='/fulfill-list/ticket-list/list-item/eventTicketConte‌​nt/xmlTicketContent/‌​ticketdescription'/> (or perhaps <xsl:copy-of select='/fulfill-list/ticket-list/list-item/eventTicketConte‌​nt/xmlTicketContent/‌​ticketdescription/node()'/>).
As first i wanted to thanks you all to have taken the time to answer me. I'm very very very glad to see SO community is such strong.
I have solved my problem this way :
<xsl:for-each select="/fulfill-list/ticket-list/list-item/eventTicketContent/xmlTicketContent/ticketdescription/node()">
<xsl:copy-of select="child::node()" />
</xsl:for-each>
I don't really know what happened beneath the hood and why the <fo> tags were removed, but they were. Looping through all of them and using <xsl:copy-of> did the trick.
Once again, a big thanks to y'all !

ignore a case and apply templates

I've the below sample line of XML.
Case1:
<para><content-style font-style="bold">1.54</content-style> For the purposes of this book, the only authorities that are strictly speaking decisive are cases decided by the Singapore courts and their predecessors, and the earlier binding decisions of the Privy Council. This relative freedom from authority has its good and bad points. On the minus side, there is often a penumbra of uncertainty surrounding a proposition based upon a foreign case; until our courts have actually accepted the proposition, it can only be treated as tentative. On the plus side, we are not bound to follow a case that is wrong in principle or weak in reasoning. Our courts are at liberty to develop and interpret the law in a manner that is suitable to Singapore’s needs.<page num="17"/></para>
Case 2:
<para><page num="5"/><content-style font-style="bold">1.12</content-style> In the context of the PA, the term ‘firm’ refers collectively to those who entered into partnership with one another and the name under which partners carry on their business (i.e. name of their partnership) is referred to as the
Case3:
<para><page num="5"/><content-style font-style="bold">1.12</content-style> In the context of the PA, the term ‘firm’ refers collectively to those who entered into partnership with one another and the name under which partners carry on their business (i.e. name of their partnership) is referred to as the <page num="6"/>
and i'm using the below XSLT line to apply-templates.
<xsl:apply-templates select="child::node()[not(self::content-style[1] and self::content-style[1]/preceding::page)]"/>
here what i'm trying to achieve is, to apply templates to para content leaving the first page that is first child node of para preceded by content-style and apart from this, though there are any other page the template should work fine. but here in my case, the page, first child of para preceding content-style is also getting caught.
please let me know where am i going wrong.
here in the cases, the output for case one should be catching the page and in t he second case the page should not be caught and int he case 3, page num="5" should be ignored and page num="6" should be caught
Thanks
Ignoring the criteria for page initially, your current condition to check for the first content-style is not going to work...
<xsl:apply-templates select="child::node()[not(self::content-style[1])]" />
This will be true for all content-style elements. The [1] condition in this case is not the position of the node within its parent, but relates to the node just selected, and will be evaluated for each content-style separately. Therefore, the above code simply won't do what you expect.
To test for equality of nodes, consider first setting a variable to hold the unique id of the first content-style
<xsl:variable name="content" select="generate-id(content-style[1])" />
Then, your xsl:apply-templates would initially look like this
<xsl:apply-templates select="child::node()[not(generate-id() = $content)]" />
And to expand this to cope with the page element, check the first following content-style also does not have the same id..
<xsl:apply-templates select="child::node()
[not(generate-id() = $content or self::page[generate-id(following-sibling::content-style[1]) = $content])]"/>
An alternative approach is also possible. Rather than actively select only the nodes you want, just select all nodes, but have template matches to exclude the nodes you don't want. Replace you xsl:apply-tempates with just this...
<xsl:apply-templates />
Then add the following two templates in your code:
<xsl:template match="content-style[not(preceding-sibling::content-style)]" />
<xsl:template match="page[following-sibling::content-style and not(preceding-sibling::content-style)]" />

How to use contains() with a set of strings in XSLT

I have the following XML snippet:
<figure customer="ABC DEF">
<image customer="ABC"/>
<image customer="XYZ"/>
</figure>
I'd like to check if the figure element's customer attribute contains the customer attributes of the image elements.
<xsl:if test="contains(#customer, image/#customer)">
...
</xsl:if>
I get an error saying:
a sequence of more than one item is not allowed as the second argument of contains
It's important to note that I cannot tell the values of the customer attributes in advance, thus using xsl:choose is not an option here.
Is it possible to solve this without using xsl:for-each?
In XSLT 2.0 you can use:
test="image/#customer/contains(../../#customer, .) = true()"
and you will get a true() result if any of them are true. Actually, that leads me to suggest:
test="some $cust in image/#customer satisfies contains(#customer, $cust)"
but that won't address the situation where the customer string is a subset of another customer string.
Therefore, perhaps this is best:
test="tokenize(#customer,'\s+') = image/#customer"
... as that will do a string-by-string comparison and give you true() if any of the tokenized values of the figure attribute is equal to one of the image attributes.

Idiom for templating similar to other templating engines like Velocity?

I have an XSLT(2.0) file; which takes an input XML data file and creates DDL/SQL Statements.
It works just fine. But it is a bit difficult to maintain, as it contains a lot of formatting information in 'concat' statements like this:
<xsl:value-of select="concat('CREATE USER ',$username,' IDENTIFIED BY ',$password,';',$nl)"/>
What I would prefer to do would be to encode my SQL Statements in a manner like this instead:
<some-enclosing-elements>[...]CREATE USER <username/>, identified by <password/>; [literally a newline here][...]</some-enclosing-elements>
I would perhaps keep this format above in the XML data file itself in a 'lookup' table at the top of the either the XSLT or the data document iself (I can't work out which yet).
Is there a standard idiom that would allow this kind of templating ?
Any ideas ?
By the way; the data document contains many different users to create of course
The AVT approach is just a little bit too devious for my taste. I tend to rely on the implicit concatenation done (in 2.0) by xsl:value-of:
<xsl:value-of select="'CREATE USER', $username, 'identified by', $password"/>
Another approach which I have used in applications where this kind of text templating is significant is to essentially write my own templating engine within XSLT; have a "message file" containing message templates in the form
<message nr="1">CREATE USER <p:user/> IDENTIFIED BY <p:password/></message>
and then write template rules to expand the messages by substituting the parameters.
#xiaoyi is right, showing the main alternative to using concat(). However that's even more notation-heavy than the concat(), since you have to keep repeating <xsl:value-of select="..." />.
A nice alternative would be to use attribute value templates (AVTs):
[...]CREATE USER {username}, identified by {password};
[...]
But ATVs are only available for (certain) attributes, not for text nodes (directly). How do you use them for this purpose?
One way in XSLT 2.0 would be to use an AVT to create a new literal result element with an attribute; specify the value of that attribute using an AVT; and then select the value of the new attribute:
<xsl:variable name="query">
<dummy val="[...]CREATE USER {username}, identified by {password};
[...]" />
</xsl:variable>
<xsl:value-of select="$query//#val" />
Yes that's some significant overhead per formatted string, but there's very little overhead per field within the string. You could do several strings together like this:
<xsl:variable name="queries">
<q val="[...]CREATE USER {username}, identified by {password};
[...]" />
<q val="[...]CREATE TABLE {tablename}, blah blah;
[...]" />
</xsl:variable>
<xsl:value-of select="$queries/q[1]/#val" />
<xsl:value-of select="$queries/q[2]/#val" />
You could use position indices as above, or use an id attribute to identify each string.
I have not seen this method advocated elsewhere, so I'd be curious to hear what others think about it.
Never mind, except...
Given the simpler approach shown by Michael Kay's answer, I don't think there's any point in doing it this way. I guess that explains why others haven't advocated this method. :-)
The only situation I can think of where this approach might still be of use is if you can't use XSLT 2.0, but you do have access to the nodeset() extension function (e.g. in IE or .NET environment). In that case you would need to wrap nodeset( ) around $queries wherever you used it in an XPath expression before /.

XSLT 1.0 Regular Expression in for loop

So I have xml like
<depot id="D7">
<address>
<number>6000</number>
<street>Yonge</street>
<city>Toronto</city>
<province>ON</province>
<postal_code>M2M 2E4</postal_code>
</address>
</depot>
I have hundreds of there depot in xml
now in my xsl, I have defined a variable called 'locale' that stores a postal code like "M1C".
After this I want to select only those depot where the postal_code is like 'locale'. In other words, If I specify locale to be "M1C", then I should get all the depot whose postal_code contains "M1C", so depot with "M1C A18", "M1C B2C", etc all should be in the result.
Currently I have the line below
< xsl:for-each select="depot[address[postal_code=$locale]]">
which gives me only depot with exact postal code match and not the ones with "M1C A18", "M1C B2C", etc. I want to use something like
<xsl:for-each select="depot[address[postal_code=*$locale*]]">
with wildcards but it does not works. Suggestions?
Use:
depot[starts-with(address/postal_code, $locale)]
Here we assume that any depot has a single address/postal_code descendent and that no possible value of $locale is a prefix of any other possible value of $locale.
If, for example, the second assumption isn't true, then use:
depot[starts-with(address/postal_code, concat($locale, ' '))]
True regular expression capabilities are available in XPath 2.0 (such as the matches() function), but they aren't necessary for a simple problem as this one.
Use this:
<xsl:for-each select="depot[contains(address/postal_code,$locale)]" />
to match only depot elements that contain the fragment defined in $locale.