I have some trouble with selecting values in node set. I have a string variable,
which concatenate in path of next existed node in xml. But when i try to select valu from it,
it results in paste of value of this variable, not the value of node. I can`t find how i can
convert string to node set for proper selection. Please, help.
<xsl:for-each select="result/node()">
<xsl:copy>
<xsl:for-each select="./node()">
<xsl:copy>
<xsl:attribute name="rating">
<xsl:text>0</xsl:text>
</xsl:attribute>
<xsl:choose>
<xsl:when test="translate(
substring(.,1,3),
$upCase,
$lowCase
) = 'id_'">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="cval"
select="concat(
'/survey/checkbox_value/',
local-name(),
'/.'
)" />
<xsl:value-of select="$cval" />
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
You don't need to convert anything. It could be done with plain xpath.
<xsl:variable name="local_name" select="local-name()"/>
<xsl:value-of select="/survey/checkbox_value/node()[
local-name() = $local_name
]"/>
Related
I am trying to use saxon:evaluate to reduce repeated code in an xsl function.
However anything I try returns an error.
This is a section of the repetitive code.
<!--Select by outputclass and group by attribute-->
<xsl:when test="$from='oclass-attribute'">
<xsl:for-each-group select="$compose//*[contains(#outputclass,$sel)]" group-by="#*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:when>
<!--Select by node and group by child node-->
<xsl:when test="$from='node-childnode'">
<xsl:for-each-group select="$compose//*[name()=$sel]" group-by="child::*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:when>
What I want is to pass a parameter dictating whether it is an element-name or outputclass-attribute that is selected.
Then another parameter dictating what to group by: attribute or parent, child, following or preceding node.
What I have tried is below:
<xsl:variable name="oSel">
<xsl:choose>
<xsl:when test="starts-with($from,'oclass-')">
<xsl:value-of select="$compose//*[contains(#outputclass,$sel)]"/>
</xsl:when>
<xsl:when test="starts-with($from,'node-')">
<xsl:value-of select="$compose//*[name()=$sel]"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="oGroup">
<xsl:choose>
<xsl:when test="ends-with($from,'-attribute')">
<xsl:value-of select="*/#*[local-name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-childnode')">
<xsl:value-of select="*/*[name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-parentnode')">
<xsl:value-of select="parent::*[name()=$group]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-followingnode')">
<xsl:value-of select="following-sibling::*[name()=$group][1]"/>
</xsl:when>
<xsl:when test="ends-with($from,'-precedingnode')">
<xsl:value-of select="preceding-sibling::*[name()=$group][1]"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="test">
<!--<xsl:for-each-group select="saxon:evaluate($oSel)" group-by="saxon:evaluate($oGroup)">-->
<xsl:for-each-group select="saxon:evaluate($oSel)" group-by="saxon:evaluate($oGroup)">
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
</xsl:variable>
I have looked into the saxon documentation and tried all sorts of solutions but none of them are working. Is it possible to do this?
Should have added that I am using Saxon 9.1.0.8 - Sorry, still new to XSLT
Firstly, saxon:evaluate() isn't the right tool for the job.
Now, why isn't it working? To declare the value of $oSel, you've done something like this:
<xsl:value-of select="$compose//*[contains(#outputclass,$sel)]"/>
which evaluates the expression in the select attribute and returns its result. But you're then passing $oSel to saxon:evaluate(), which expects a string containing an XPath expression. I think you're trying to bind the variable to the expression "$compose//*[contains(#outputclass,$sel)]", not to the result of evaluating this expression. To do that you would have to write
<xsl:value-of select="'$compose//*[contains(#outputclass,$sel)]'"/>
Note the extra quotes; but that would now fail because the expression passed to saxon:evaluate() can't explicitly use variables such as $compose (there's a mechanism to pass parameters, but you don't really want to go there).
In XSLT 3.0 saxon:evaluate is superseded by the standard instruction xsl:evaluate; but you don't want that one either.
The right mechanism to be using here is higher order functions.
In XSLT 3.0 you can write
<xsl:for-each-group select="$compose//*[$predicate(.)]" group-by="$grouping-key(.)">
Where $predicate and $grouping-key are variables bound to user-defined functions. You can bind these variables with logic like this:
<xsl:variable name="predicate" as="function(element()) as xs:boolean">
<xsl:choose>
<xsl:when test="starts-with($from,'oclass-')">
<xsl:sequence select="function($n){contains($n/#outputclass,$sel)}"/>
</xsl:when>
<xsl:when test="starts-with($from,'node-')">
<xsl:sequence select="function($n){name($n)=$sel}"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
Change the xpath as below.
$compose//*[name()=$sel or contains(#outputclass,$sel)]
Here is the final code looks like
<xsl:when test="$from='oclass-attribute'">
<xsl:for-each-group select="$compose//*[name()=$sel or contains(#outputclass,$sel)]" group-by="#*[name()=$group]">
<xsl:sort select="current-grouping-key()" />
<element key="{current-grouping-key()}">
<xsl:if test="number(current-group()[1])=number(current-group()[1])">
<xsl:attribute name="max" select="max(current-group())"/>
<xsl:attribute name="sum" select="sum(current-group())"/>
</xsl:if>
<xsl:copy-of select="current-group()[1]"/>
</element>
</xsl:for-each-group>
Try
select="if ($from='oclass-attribute') then $compose//*[contains(#outputclass,$sel)] else $compose//*[name()=$sel]"
and use the same approach for the group-by attribute:
group-by="if ($from='oclass-attribute') then #*[name()=$group] else child::*[name()=$group]"
I am trying to update an old form that uses a data model that has changed, so any reference to the old model I want to replace with the new. I currently use the function matches to tell if I should preform the replace on the current string and then use the replace function to replace the value with the new one, the problem is that the regex used in the matches does not work with the regex in the replace.
<xsl:template
match="//*[contains(#*:default,'instance(''document'')/')
mode="pass">
<xsl:variable
name="regex"
as="element()*">
<regex>instance('document')/doc_type/description</regex>
<regex>anotherRegex</regex>
</xsl:variable>
<xsl:variable
name="replacement"
as="element()*">
<replacement>xxf:get-request-parameter('documentDesc')</replacement>
<replacement>replacedRegex</replacement>
</xsl:variable>
<xsl:element name="{name()}">
<xsl:for-each select="#*">
<xsl:choose>
<xsl:when test="name() = ('xxf:default')">
<xsl:attribute name="xxf:default">
<xsl:analyze-string
regex="{concat('(',$regex[1],'|',$regex[2],')')}"
select=".">
<xsl:matching-substring>
<xsl:if test="matches(.,$regex[1])">
<xsl:value-of select="replace(.,$regex[1],$replacement[1])" />
</xsl:if>
<xsl:if test="matches(.,$regex[2])">
<xsl:value-of select="replace(.,$regex[2],$replacement[2])" />
</xsl:if>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="." />
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="{local-name()}"><xsl:value-of select="." /></xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:element>
</xsl:template>
Current XML:
<xf:bind id="clinic-bind"
name="clinic"
xxf:default="instance('document')/doc_type/description"
type="xf:string"/>
What I want to turn it into:
<xf:bind id="clinic-bind"
name="clinic"
xxf:default="xxf:get-request-parameter('documentDesc')"
type="xf:string"/>
So the Problem was basicaally I had to escape the '()' characters in the regex variable.
So I ended up with
<regex>instance\('document'\)/doc_type/description</regex>
In the regex part.
Please help me to find the mmlmsubsup's third child starts with mmlmi with attribute.
Find the XML given below, suggest to remove the particular element based on its position and parent.
<article><mmlmath><mmlmsubsup><mmlmrow><mmlmi>A</mmlmi></mmlmrow><mmlmrow><mmlmi>b</mmlmi></mmlmrow><mmlmrow><mmlmi mathcolor="magenta">#</mmlmi><mmlmo>(</mmlmo><mmlmo>c</mmlmo><mmlmo>)</mmlmo></mmlmrow></mmlmsubsup></mmlmath></article>
SubSup.xslt:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="mmlmsubsup">
<mmlmsubsup>
<xsl:for-each select="child::*[1]">
<xsl:choose>
<xsl:when test="child::*[1][name()='mmlmrow']">
<xsl:copy><xsl:apply-templates/></xsl:copy>
</xsl:when>
<xsl:otherwise><xsl:copy><xsl:apply-templates/></xsl:copy></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:for-each select="child::*[2]">
<xsl:choose>
<xsl:when test="child::*[1][name()='mmlmrow']">
<xsl:for-each select="mmlmrow">
<xsl:if test="child::*[1][name()='mmlmi'][#mathcolor]">delete</xsl:if>
<xsl:if test="not(child::*[name()='mmlmi'][#mathcolor])"><xsl:copy><xsl:apply-templates/></xsl:copy></xsl:if>
</xsl:for-each>
<xsl:copy><xsl:apply-templates/></xsl:copy>
</xsl:when>
<xsl:otherwise><xsl:copy><xsl:apply-templates/></xsl:copy></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:for-each select="child::*[3]">
<xsl:choose>
<xsl:when test="child::*[1][name()='mmlmrow']">
<xsl:for-each select="mmlmrow">
<xsl:if test="child::*[1][name()='mmlmi'][#mathcolor]">delete</xsl:if>
<xsl:if test="not(child::*[name()='mmlmi'][#mathcolor])"><xsl:copy><xsl:apply-templates/></xsl:copy></xsl:if>
</xsl:for-each>
<xsl:copy><xsl:apply-templates/></xsl:copy>
</xsl:when>
<xsl:otherwise><xsl:copy><xsl:apply-templates/></xsl:copy></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</mmlmsubsup>
</xsl:template>
</xsl:stylesheet>
Required OutPut:
Remove the "mmlmi" element of attribute "mathcolor", if mmlmi found as first child for second and third mmlmrow of mmlmsubsub ancetor.
Remove the "mmlmi" element of attribute "mathcolor", if mmlmi found as
first child for second and third mmlmrow of mmlmsubsub ancetor.
I think that translates to:
<xsl:template match="mmlmi[#mathcolor]
[local-name(ancestor::mmlmsubsup/mmlmrow[2]/*[1])='mmlmi']
[local-name(ancestor::mmlmsubsup/mmlmrow[3]/*[1])='mmlmi']" />
It's hard to be sure because (1) the formulation is not quite clear, and (2) the source XML is not good testing material.
I'm trying to get the value of a text string of page, assign a url to it, and then display the result on a different page using xslt. I'm not sure how to get the value of the textstring.
Here is my macro:
<!-- Input the related links property alias here -->
<xsl:variable name="fieldName" select="/macro/fieldName"/>
<xsl:template match="/">
<xsl:if test="$fieldName != ''">
<!-- The fun starts here -->
<xsl:element name="a">
<xsl:if test="./new-window[. = 'True']">
<xsl:attribute name="target">_blank</xsl:attribute>
</xsl:if>
<xsl:attribute name="href">
<xsl:value-of select="/myUrl.aspx"/>
</xsl:attribute>
<xsl:value-of select="./#title"/>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Found it! select="$parent/*[name() = $fieldName]
I'm using an XSLT to transform from one XML standard to another. The particular resulting XML standard contains a root element which is part of a namespace and a child node which is part of another namepsace.
The transform successfully reflects these namespaces but the child's child now contains a blank xmlns attribute. How can I prevent this xmlns=""?
XSLT Snippet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:apply-templates select="REQUEST_GROUP" />
</xsl:template>
<xsl:template match="REQUEST_GROUP">
<ONCORE_ERECORD xmlns="http://test.com">
<xsl:apply-templates select="REQUEST/PRIA_REQUEST/PACKAGE"/>
<PAYMENT PaymentType="ACH" />
<TRANSACTION_INFO _AgentKey="" _AgentPassword="" />
</ONCORE_ERECORD>
</xsl:template>
<xsl:template match="PACKAGE">
<DOCUMENT_RECORDATION xmlns="http://test2.org">
<xsl:apply-templates select="PRIA_DOCUMENT"/>
</DOCUMENT_RECORDATION>
</xsl:template>
<xsl:template match="PRIA_DOCUMENT">
<PRIA_DOCUMENT _PRIAVersion="1.2">
<xsl:attribute name="_Type">
<xsl:value-of select="#RecordableDocumentType"/>
</xsl:attribute>
<xsl:attribute name="_Code"/>
<xsl:apply-templates select="GRANTOR" />
<xsl:apply-templates select="GRANTEE" />
<xsl:choose>
<xsl:when test="count(PROPERTY) = 0">
<PROPERTY>
<xsl:attribute name="_StreetAddress">
<xsl:value-of select="#StreetAddress"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress2">
<xsl:value-of select="#StreetAddress2"/>
</xsl:attribute>
<xsl:attribute name="_City">
<xsl:value-of select="#City"/>
</xsl:attribute>
<xsl:attribute name="_State">
<xsl:value-of select="#State"/>
</xsl:attribute>
<xsl:attribute name="_PostalCode">
<xsl:value-of select="#PostalCode"/>
</xsl:attribute>
<xsl:attribute name="_County">
<xsl:value-of select="#County"/>
</xsl:attribute>
<xsl:apply-templates select="LEGAL_DESCRIPTION"/>
</PROPERTY>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PROPERTY" />
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="count(PARTIES) = 0">
<PARTIES>
<_RETURN_TO_PARTY _UnparsedName="" _StreetAddress="" _StreetAddress2="" _City="" _State="" _PostalCode="" />
</PARTIES>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PARTIES" />
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select="EXECUTION" />
<xsl:apply-templates select="CONSIDERATION" />
<xsl:apply-templates select="RECORDABLE_DOCUMENT/_ASSOCIATED_DOCUMENT" />
<xsl:apply-templates select="EMBEDDED_FILE" />
</PRIA_DOCUMENT>
Source XML:
<REQUEST_GROUP PRIAVersionIdentifier="2.4">
<REQUEST>
<PRIA_REQUEST _Type="RecordDocuments">
<PACKAGE>
<PRIA_DOCUMENT PRIAVersionIdentifier="2.4" RecordableDocumentSequenceIdentifier="1" RecordableDocumentType="Mortgage">
Resulting XML:
<?xml version="1.0" encoding="utf-8"?>
<ONCORE_ERECORD xmlns="http://test.com">
<DOCUMENT_RECORDATION xmlns="http://test2.org">
<PRIA_DOCUMENT _PRIAVersion="1.2" _Type="Mortgage" _Code="" xmlns="">
This is happening because PRIA_DOCUMENT is in the default namespace, while its parent DOCUMENT_RECORDATION is in a non-default namespace. You must put the PRIA_DOCUMENT in the same namespace as its parent, otherwise the serializer is required to generate xmlns="".
.
.
<xsl:template match="PRIA_DOCUMENT">
<PRIA_DOCUMENT _PRIAVersion="1.2" xmlns="http://pria.org">
.
.
.
See Michael Kay's "XSLT 2.0 and XPATH 2.0, 4th edition", page 475 where he discusses this exact situation.
I found a solution that worked, though it may not have been the most efficient way to achieve the desired results.
I simply changed all literal element declarations to:
</xsl:element>
and declared the namespace. The resulting xslt is as follows:
<xsl:template match="REQUEST_GROUP">
<xsl:element name="ONCORE_ERECORD" namespace="http://test.com">
<xsl:apply-templates select="REQUEST/PRIA_REQUEST/PACKAGE"/>
<xsl:element name="PAYMENT" namespace="http://test.com">
<xsl:attribute name="PaymentType">
<xsl:value-of select="'ACH'"/>
</xsl:attribute>
</xsl:element>
<xsl:element name="TRANSACTION_INFO" namespace="http://test.com">
<xsl:attribute name="_AgentKey">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_AgentPassword">
<xsl:value-of select="''"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="PACKAGE">
<xsl:element name="DOCUMENT_RECORDATION" namespace="http://test2.org">
<xsl:apply-templates select="PRIA_DOCUMENT"/>
</xsl:element>
</xsl:template>
<xsl:template match="PRIA_DOCUMENT">
<xsl:element name="PRIA_DOCUMENT" namespace="http://test2.org">
<xsl:attribute name="_PRIAVersion">
<xsl:value-of select="'1.2'"/>
</xsl:attribute>
<xsl:attribute name="_Type">
<xsl:value-of select="#RecordableDocumentType"/>
</xsl:attribute>
<xsl:attribute name="_Code"/>
<xsl:apply-templates select="GRANTOR" />
<xsl:apply-templates select="GRANTEE" />
<xsl:choose>
<xsl:when test="count(PROPERTY) = 0">
<xsl:element name="PROPERTY" namespace="http://test2.org">
<xsl:attribute name="_StreetAddress">
<xsl:value-of select="#StreetAddress"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress2">
<xsl:value-of select="#StreetAddress2"/>
</xsl:attribute>
<xsl:attribute name="_City">
<xsl:value-of select="#City"/>
</xsl:attribute>
<xsl:attribute name="_State">
<xsl:value-of select="#State"/>
</xsl:attribute>
<xsl:attribute name="_PostalCode">
<xsl:value-of select="#PostalCode"/>
</xsl:attribute>
<xsl:attribute name="_County">
<xsl:value-of select="#County"/>
</xsl:attribute>
<xsl:apply-templates select="LEGAL_DESCRIPTION"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PROPERTY" />
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="count(PARTIES) = 0">
<xsl:element name="PARTIES" namespace="http://test2.org">
<xsl:element name="_RETURN_TO_PARTY" namespace="http://test2.org">
<xsl:attribute name="_UnparseName">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_StreetAddress2">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_City">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_State">
<xsl:value-of select="''"/>
</xsl:attribute>
<xsl:attribute name="_PostalCode">
<xsl:value-of select="''"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="PARTIES" />
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select="EXECUTION" />
<xsl:apply-templates select="CONSIDERATION" />
<xsl:apply-templates select="RECORDABLE_DOCUMENT/_ASSOCIATED_DOCUMENT" />
<xsl:apply-templates select="EMBEDDED_FILE" />
</xsl:element>
</xsl:template>
I was having a similar issue even declaring the namespace on the child elements but was still ending up with
xmlns=""
I thought it was due to the xslt transformation but the string result of the transform was correct and it was when I then transformed the string to a org.w3c.dom.Document that the default namespaces were being added.
Making the DocumentBuilderFactory namespace aware fixed this
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document metadataDOM = db.parse(new ByteArrayInputStream(stringWriter.toString().getBytes()));
Put the calling template and the applied template in the same namespace.
You're re-defining the default namespace with each of those nodes with the 'xmlns=' declaration. Because the PRIA_DOCUMENT doesn't have a namespace, the output needs to redeclare it as empty, or it would have the same namespace as it's parent. I'd recommend adding in a named namespace to those elements that have one defined, for example:
<pria:DOCUMENT_RECORDATION xmlns:pria="http://pria.org">
and
<as:ONCORE_ERECORD xmlns:as="http://aptitudesolutions.com">
With these named namespaces in place, the blank declaration on the PRIA_DOCUMENT element becomes unnecessary, and is not added.