I am trying to filter out a username for an incoming connection with .xsl file(DataPower appliance). I have 4 scenarios
user#domain.com - needs to stay the way it is.
user#remove.com - need to remove the domain part.
user#domain.com#remove.com - need to remove only the remove part.
user#remove.com.anything - again need to remove this and everything after.
There are 3 variables here. The 'user' can be anything. The domain can be anything. And the .anything after the remove.com, can be anything. #remove.com will ALWAYS be the same. Luckily that is the constant we can use.
Is there a simple if/then statement we can use to do this. We have tried many variations of If, when, contains, and can't seem to get it working. I can paste the code we are currently using if that will help.
Thanks.
A good solution for your problem can be Regex expressions, try "regexp:match(yourExpressionGoesHere)"
and then you just have to write the specific regex for each case.
We figured it out. Sorry to jump the gun, we've been at this a few days.
Here's the code:
<xsl:template match="#* | node()">
<xsl:variable name="userid" select="substring-after(/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='Authenticate']/*[local-name()='username'],'#')"/>
<xsl:choose>
<xsl:when test="contains($userid,'remove')">
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:*deleted*">
<soapenv:Header />
<soapenv:Body>
<urn:authenticate>
<userId0>
<xsl:value-of select="substring-before(/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='Authenticate']/*[local-name()='username'],'#remove.com')"/>
</userId0>
<credential>
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='Authenticate']/*[local-name()='password']"/>
</credential>
</urn:authenticate>
</soapenv:Body>
</soapenv:Envelope>
</xsl:when>
<xsl:otherwise>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:*deleted*">
<soapenv:Header />
<soapenv:Body>
<urn:authenticate>
<userId0>
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='Authenticate']/*[local-name()='username']"/>
</userId0>
<credential>
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='Authenticate']/*[local-name()='password']"/>
</credential>
</urn:authenticate>
</soapenv:Body>
</soapenv:Envelope>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Related
can anyone help me to find a way to extract some parameters from URL and save pass it into soap request
URL
https://hostname/ServiceName?Consumer=ABS&ID=3231
I'd take only Consumer name which is ABS and it's id = 3231 and put it into soap as below
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<Consumer>ABS</Consumer>
<ID>3231<ID>
</soapenv:Body>
</soapenv:Envelope>
In XSLT 2.0 or higher, you can use the xsl:analyze-string instruction to extract all query parameters from a URL string:
<xsl:analyze-string select="$URL" regex="[\?&]([^=]+)=([^&]+)">
<xsl:matching-substring>
<xsl:element name="{regex-group(1)}">
<xsl:value-of select="regex-group(2)" />
</xsl:element>
</xsl:matching-substring>
</xsl:analyze-string>
Demo: http://xsltfiddle.liberty-development.net/a9GPfr
if you want to use string formatting functions along with hard coded element name in result
<xsl:variable name="URL">https://hostname/ServiceName?Consumer=ABS&ID=3231</xsl:variable>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<Consumer>
<xsl:value-of select="substring-before(substring-after($URL,'Consumer='),'&')"/>
</Consumer>
<ID>
<xsl:value-of select="substring-after($URL,'ID=')"/>
</ID>
</soapenv:Body>
</soapenv:Envelope>
I have the requirement in which I need to match values
Buyer,BilltoParty,Shipto which can the value AGIIS-EBID or EAN or GLN
I am using XPath1.0 to extract these values and use the condition.
I am able to get correct value for the Buyer*
The problem I am facing is for Shipto and Billtoparty
Xpath for getting value for Shipto and Billto
//*[local-name()='ShipNotice']/*[local-name()='ShipNoticeBody']/*[local-name()='ShipNoticePartners']/*[local-name()='OtherPartner']/*[local-name()='PartnerInformation']/*[local-name()='PartnerIdentifier']/#*[local-name()='Agency']
Which gives the value as:
Agency AGIIS-EBID
Agency SCAC
Agency AGIIS-EBID
Agency AGIIS-EBID
INPUT XML Updated one
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:ces:names:specification:ces:schema:all:4:0">
<soapenv:Header/>
<soapenv:Body>
<ces:ShipNotice Version="4.0" xmlns:ces="urn:ces:names:specification:ces:schema:all:4:0">
<ces:Header>
<ces:From>
</ces:From>
<ces:To>
</ces:To>
</ces:Header>
<ces:ShipNoticeBody>
<ces:ShipNoticePartners>
<ces:Buyer>
<ces:PartnerInformation>
<ces:PartnerIdentifier Agency="AGIIS-EBID">8049915600000</ces:PartnerIdentifier>
</ces:PartnerInformation>
</ces:Buyer>
<ces:OtherPartner PartnerRole="ShipFrom">
<ces:PartnerInformation>
<ces:PartnerIdentifier Agency="AGIIS-EBID">0447026370000</ces:PartnerIdentifier>
</ces:PartnerInformation>
</ces:OtherPartner>
<ces:OtherPartner PartnerRole="Carrier">
<ces:PartnerInformation>
<ces:PartnerIdentifier Agency="SCAC">B935</ces:PartnerIdentifier>
</ces:PartnerInformation>
</ces:OtherPartner>
<ces:OtherPartner PartnerRole="ShipTo">
<ces:PartnerInformation>
<ces:PartnerIdentifier Agency="AGIIS-EBID">8049915600000</ces:PartnerIdentifier>
</ces:PartnerInformation>
</ces:OtherPartner>
<ces:OtherPartner PartnerRole="BillToParty">
<ces:PartnerInformation>
<ces:PartnerIdentifier Agency="AGIIS-EBID">1024122440000</ces:PartnerIdentifier>
</ces:PartnerInformation>
</ces:OtherPartner>
</ces:ShipNoticePartners>
<ces:ShipNoticeDetails>
</ces:ShipNoticeDetails>
</ces:ShipNoticeBody>
</ces:ShipNotice>
</soapenv:Body>
</soapenv:Envelope>
Below is the code I am using.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dpconfig="http://www.datapower.com/param/config" xmlns:cidx="urn:cidx:names:specification:ces:schema:all:4:0" xmlns:dp="http://www.datapower.com/extensions" extension-element-prefixes="dp">
<xsl:param name="dpconfig:AGIIS-EBID" select=" 'AGIIS-EBID' "/>
<xsl:param name="dpconfig:EAN" select="'EAN'"/>
<xsl:param name="dpconfig:GLN" select="'GLN'"/>
<xsl:template match="/">
<xsl:copy-of select="."/>
<xsl:variable name="Buyer" select="//*[local-name()='ShipNotice']/*[local-name()='ShipNoticeBody']/*[local-name()='ShipNoticePartners']/*[local-name()='Buyer']/*[local-name()='PartnerInformation']/*[local-name()='PartnerIdentifier']/#*[local-name()='Agency']"/>
<xsl:variable name="Shipto" select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ShipNotice']/*[local-name()='ShipNoticeBody']/*[local-name()='ShipNoticePartners']/*[local-name()='OtherPartner']/*[local-name()='PartnerInformation']/*[local-name()='PartnerIdentifier']/#*[local-name()='Agency']"/>
<xsl:variable name="Billto" select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ShipNotice']/*[local-name()='ShipNoticeBody']/*[local-name()='ShipNoticePartners']/*[local-name()='OtherPartner']/*[local-name()='PartnerInformation']/*[local-name()='PartnerIdentifier']/#*[local-name()='Agency']"/>
<xsl:choose>
<xsl:when test="$Buyer='AGIIS-EBID' and $Shipto='AGIIS-EBID' and $Billto='AGIIS-EBID' or $Buyer='EAN' and $Shipto='EAN' and $Billto='EAN' or $Buyer='GLN' and $Shipto='GLN' and $Billto='GLN'">
</xsl:when>
<xsl:otherwise>
Partner didn't match the AGIIS-EBID or EAN or GLN
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Can anyone please help me on Xpath and also let me know the way I am doing the condition match is correct or not?
Expecting output
I am expecting the variables Buyer, Shipto, Billto should match the value == AGIIS-EBID, but the Xpath which I am using gives
Agency AGIIS-EBID
Agency SCAC
Agency AGIIS-EBID
Agency AGIIS-EBID
From looking at your XSLT, and seeing that your Shipto and Billto variables have the same declaration, I think what you are saying is that these currently match four nodes (or rather attributes), but you only want one.
Specifically, from looking at your XML, it looks like the crucial information is on the OtherPartner node
<ces:OtherPartner PartnerRole="ShipTo">
<ces:OtherPartner PartnerRole="BillToParty">
This means you need to add an extra condition after the current condition that matches OtherPartner. For example, for ShipTo the condition would be this:
<xsl:variable name="Shipto" select="
/*[local-name()='Envelope']
/*[local-name()='Body']
/*[local-name()='ShipNotice']
/*[local-name()='ShipNoticeBody']
/*[local-name()='ShipNoticePartners']
/*[local-name()='OtherPartner'][#PartnerRole='ShipTo']
/*[local-name()='PartnerInformation']
/*[local-name()='PartnerIdentifier']
/#*[local-name()='Agency']"/>
Of course, as pointed out in comments, if you know the namespace uri in the XML, it is preferable to declare them in the XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dpconfig="http://www.datapower.com/param/config"
xmlns:cidx="urn:cidx:names:specification:ces:schema:all:4:0"
xmlns:dp="http://www.datapower.com/extensions"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ces="urn:ces:names:specification:ces:schema:all:4:0" extension-element-prefixes="dp">
Then you can "simplify" your variable to this for example
<xsl:variable name="Shipto" select="
soapenv:Envelope
/soapenv:Body
/ces:ShipNotice
/ces:ShipNoticeBody
/ces:ShipNoticePartners
/ces:OtherPartner[#PartnerRole='ShipTo']
/ces:PartnerInformation
/ces:PartnerIdentifier/#Agency"/>
And similary for your Billto variable. Just change the condition on the #PartnerRole attribute.
Additionally, you might want to consider putting some parentheses around you final condition
<xsl:when test="
($Buyer='AGIIS-EBID' and $Shipto='AGIIS-EBID' and $Billto='AGIIS-EBID')
or ($Buyer='EAN' and $Shipto='EAN' and $Billto='EAN')
or ($Buyer='GLN' and $Shipto='GLN' and $Billto='GLN')">
Or better still do this...
<xsl:when test="($Buyer='AGIIS-EBID' or $Buyer='EAN' or $Buyer='GLN')
and $Shipto=$Buyer
and $Billto=$Buyer">
I have XML list of values that will be populated by a form. Based on the value of a particular element, in this case Type, I need to dynamically construct a SOAP request. Only certain elements from the Source XML will be used, contingent on the Type. In addition, I need to add a namespace prefix to every element in the SOAP body that is not present or referenced in any way in the Source XML.
Given XML input in the following format:
<User>
<Action>Update</Action>
<Id>123-45-5678</Id>
<Type>Student</Type>
<Validate>true</Validate>
<Grade>11</Grade>
<Classroom/>
<UserName/>
<Password/>
</User>
If I apply the following transform:
<xsl:stylesheet version="1.0" xmlns:ztx="http://tempuri.org/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:variable name="Action" select="/User/Action"/>
<xsl:variable name="Id" select="/User/Id"/>
<xsl:variable name="Type" select="/User/Type"/>
<xsl:variable name="Method" select="concat('ztx:', $Action, $Type)"/>
<xsl:template match="/">
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<xsl:element name="{$Method}">
<xsl:if test="$Action != 'Create'">
<xsl:copy-of select="/User/Id"/>
</xsl:if>
<xsl:if test="$Action != 'Delete'">
<xsl:choose>
<xsl:when test="$Type = 'Teacher'">
<xsl:copy-of select="/User/Classroom"/>
</xsl:when>
<xsl:when test="$Type = 'Student'">
<xsl:copy-of select="/User/Grade"/>
</xsl:when>
<xsl:when test="$Type = 'Administrator'">
<xsl:copy-of select="/User/UserName"/>
<xsl:copy-of select="/User/Password"/>
</xsl:when>
</xsl:choose>
<xsl:copy-of select="/User/Validate"/>
</xsl:if>
</xsl:element>
</soapenv:Body>
</soapenv:Envelope>
</xsl:template>
</xsl:stylesheet>
I get the following output:
<soapenv:Envelope xmlns:ztx="http://tempuri.org/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<ztx:UpdateStudent>
<Id>123-45-5678</Id>
<Grade>11</Grade>
<Validate>true</Validate>
</ztx:UpdateStudent>
</soapenv:Body>
</soapenv:Envelope>
My desired output, however, is:
<soapenv:Envelope xmlns:ztx="http://tempuri.org/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<ztx:UpdateStudent>
<ztx:Id>123-45-5678</ztx:Id>
<ztx:Grade>11</ztx:Grade>
<ztx:Validate>true</ztx:Validate>
</ztx:UpdateStudent>
</soapenv:Body>
</soapenv:Envelope>
I would like to do this elegantly, if possible, and in a single transform. Looking for a generic way, perhaps with another template, to just process every element in the SOAP body and add the namespace, rather than hard-code them individually.
NOTE: I am restricted to XSLT 1.0
Thank you!
Like you mentioned in your question you can add another template to add the namespace prefix to the elements for your SOAP response.
Here is the template
<xsl:template name="addNamespace" match="User/*">
<xsl:element name="{concat('ztx:',name(.))}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
And then you would just change all of your copy-of to apply-templates then to use the new template.
Short question: How to deal with a raw ampersand in xml input file.
ADDED: Im not even selecting the field with the ampersand. The parser complains at the presence of the ampersand within the file.
Long explanation:
im dealing with xml that is generated via a url response.
<NOTE>I%20hope%20this%20won%27t%20require%20a%20signature%3f%20%20
There%20should%20be%20painters%20%26%20stone%20guys%20at%20the
%20house%20on%20Wednesday%2c%20but%20depending%20on%20what%20time%20
it%20is%20delivered%2c%20I%20can%27t%20guarantee%21%20%20
Also%2c%20just%20want%20to%20make%20sure%20the%20billing%20address
%20is%20different%20from%20shipping%20address%3f
</NOTE>
which is url decoded into this:
<NOTE>I hope this won't require a signature?
There should be painters & stone guys at the
house on Wednesday, but depending on what time it is delivered, I can't guarantee!
Also, just want to make sure the billing address is different from shipping address?
</NOTE>
The Problem:
xslproc chokes on that last string because of the '&' in "painters & stone guys"
with the following error:
xmlParseEntityRef: no name
<NOTE>I hope this won't require a signature? There should be painters &
It looks like xsltproc expects a closing </NOTE>
Ive tried all manner of disable-output-escaping="yes" in various locations. xsl:output and xsl:value-of
And also tried xsltproc --decode-uri but cant figure out that one out. No documentation.
Note:
I wonder if its worth keeping the input in urlencoded format. And using a DOCTYPE..such as the following (not sure how to do that). The output is eventually a browser.
<!DOCTYPE xsl:stylesheet [
<!ENTITY nbsp " ">
<!ENTITY copy "©">
<!ENTITY reg "®">
]>
The XML is malformed if there's an ampersand that's not escaped. If you put the string inside <![CDATA[]]>, then it should work.
<NOTE><![CDATA[I hope this won't require a signature?
There should be painters & stone guys at the
house on Wednesday, but depending on what time it is delivered, I can't guarantee!
Also, just want to make sure the billing address is different from shipping address?]]>
</NOTE>
Or, of course, use & instead of &.
Edit: You can also translate the URL escapes into numeric character references if the XSLT processor supports disable-output-escaping (and xsltproc does):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="NOTE">
<xsl:copy>
<xsl:call-template name="decodeURL"/>
</xsl:copy>
</xsl:template>
<xsl:template name="decodeURL">
<xsl:param name="URL" select="string()"/>
<xsl:choose>
<xsl:when test="contains($URL,'%')">
<xsl:variable name="remainingURL" select="substring-after($URL,'%')"/>
<xsl:value-of disable-output-escaping="yes" select="concat(
substring-before($URL,'%'),
'&#x',
substring($remainingURL,1,2),
';')"/>
<xsl:call-template name="decodeURL">
<xsl:with-param name="URL" select="substring($remainingURL,3)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$URL"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Of course you don't have to use this transformation as a preprocessing step, you can re-use decodeURL in a stylesheet that transforms your source XML that includes the URL encoded string to HTML or whatever.
my first post here - sure hope someone will know the answer!
I have been able to find solutions for many issues I had, but not for this one.
The questions and answers on this site on the same subject did not solve my issue...
I have an xml containing Format specifications like
<Format>
<TagNr>92</TagNr>
<Option>A</Option>
<Format>//[N]15d</Format>
</Format>
<Format>
<TagNr>92</TagNr>
<Option>B</Option>
<Format>//3!a/3!a/15d</Format>
</Format>
TagNr + option is a unique combination within this nodeset.
I defined a key to make using the set of formats easier:
<xsl:key name="xx" match="//Format/Format" use="concat(../TagNr, ../Option)"/>
I can indeed use this key and get the correct value, but only in non-special elements; I get an error "Error in XPath 2.0 expression Not a node item" when using this key within for-each or other constructs like the one below.
What I try to achieve is the following: In other nodes processed there is a string of options for which I wish to retrieve the format for each character.
For example:
<Tag>
<TagNr>92</TagNr>
<Options>AB</Options>
</Tag>
I have been trying lots of variants of the below but no luck:
<xsl:variable name="TN"><xsl:value-of select="TagNr"/></xsl:variable>
<xsl:variable name="optList">
<xsl:analyze-string select="./Options" regex="[A-Z]">
<xsl:matching-substring>
<xsl:variable name="TNO" select="concat($TN, .)"/>
<opt>
<tag><xsl:value-of select="$TNO"/></tag>
<fmt><xsl:value-of select="key('xx', $TNO)"/></fmt>
</opt>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:variable>
Splitting into individual characters using the regex goes fine and when retrieving (only) the value for opt/tag that goes fine too.
But when I add opt/fmt, I run into the mentioned error message for the Xpath expression select="key('xx', $TNO)".
I tried defining a variable based on the key function as suggested in another thread on this site, but did not succeed.
Can anyone help me?
The key() function (with two arguments) searches the document containing the context node. If the context item is not a node - for example, within analyze-string - then you will get this error, because it doesn't know which document to search. The answer is to use the third argument of key() to supply this information.
The problem is that the context changes in your analyze-string element. Maybe the following solution will help you.
For an XML file like that :
<a>
<Format>
<TagNr>92</TagNr>
<Option>A</Option>
<Format>//[N]15d</Format>
</Format>
<Format>
<TagNr>92</TagNr>
<Option>B</Option>
<Format>//3!a/3!a/15d</Format>
</Format>
<Tag>
<TagNr>92</TagNr>
<Options>AB</Options>
</Tag>
</a>
Consider the following XSLT :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
<xsl:output indent="yes"/>
<xsl:key name="xx" match="//Format/Format" use="concat(../TagNr, ../Option)"/>
<xsl:template match="/">
<result>
<xsl:apply-templates select="//Tag"/>
</result>
</xsl:template>
<xsl:template match="Tag">
<xsl:call-template name="createOPT">
<xsl:with-param name="str" as="xs:string" select="Options"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="createOPT">
<xsl:param name="str"/>
<xsl:if test="string-length($str) > 0">
<xsl:variable name="firstChar" select="substring($str,1,1)"/>
<xsl:variable name="TNO" select="concat(TagNr,$firstChar)"/>
<opt>
<tag><xsl:value-of select="$TNO"/></tag>
<fmt><xsl:value-of select="key('xx', $TNO)"/></fmt>
</opt>
<xsl:call-template name="createOPT">
<xsl:with-param name="str" select="substring($str,2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The result is :
<?xml version="1.0" encoding="UTF-8"?>
<result>
<opt>
<tag>92A</tag>
<fmt>//[N]15d</fmt>
</opt>
<opt>
<tag>92B</tag>
<fmt>//3!a/3!a/15d</fmt>
</opt>
</result>
The easiest XSLT 2.0 way to process a string character by character is the following:
<xsl:for-each select="string-to-codepoints($vStr)">
<xsl:variable name="$vChar" select=
"codepoints-to-string(.)"/>
<!-- Process $vChar here: -->
</xsl:for-each/>
You can combine this with saving the original document context into a variable (say $vDoc) and using this variable as the 3rd argument of the key() function -- which is again an XSLT 2.0 - only feature.
So, you'll have something like:
key('xx', concat($TN, $vChar), $vDoc)
Summary:
Use the string-to-codepoints() and codepoints-to-string() functions for char-by-char processing.
Use the 3-rd argument of the key() function to specify context different from the current.