 |
| XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead. |
Welcome to the p2p.wrox.com Forums.
You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
|
|
|
|

August 31st, 2006, 10:06 PM
|
|
Registered User
|
|
Join Date: Aug 2006
Posts: 5
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
XSL Variable and/or Parameter Question
I've received a mailing list in xml format and I'm trying to parse it into a text file. I have that portions working however, one issue I seem to have is that these records come across in a 1 to many format. For example, for one address, I may have multiple listings. So, here's an over simplified record:
1234 Anywhere St.
-Bob Smith
-Sally Smith
-John Smith
The output should be something like,
"Bob Smith", "1234 Anywhere St."
"Sally Smith", "1234 Anywhere St."
"John Smith", "1234 Anywhere St."
So, you can see from my xsl code (at the end of this post), that I need to look at the acctSect first, and then the lstgSect second. So, I guess what I thought I would do is set parameter or variable names to the respective address fields, and then when it gets to the listing portion (people's names), just populate those fields and loop through them accordingly.
The problem is, the variables are not being passed. I've also tried using the parameter element as well, with the same result.
Any thoughts on getting this to work?
XSL sheet is below:
Thanks in advance!
Matt
Quote:
quote:
<?xml version="1.0"?>
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="Data">
<xsl:apply-templates select="acctSect | lstgSect | airEnd"/>
</xsl:template>
<xsl:template match="acctSect">
<xsl:variable name="phone" select="'normalize-space(uacctId/atnNbr)'"/>
<xsl:variable name="street1" select="normalize-space(inSa/sa/astrt/ahn/ahnbrNbr)"/>
<xsl:variable name="street2" select="normalize-space(inSa/sa/astrt/astrtNm/astrtNm)"/>
<xsl:variable name="street3" select="normalize-space(inSa/sa//astrt/astrtNm/athoroTxt)"/>
<xsl:variable name="city" select="normalize-space(inSa/sa/aloc/alocNm)"/>
<xsl:variable name="state" select="normalize-space(inSa/sa/astateCd)"/>
<xsl:variable name="zip" select="normalize-space(inSa/sa/azipCd)"/>
</xsl:template>
<xsl:template match="lstgSect">
<xsl:for-each select="lstgReq">
<xsl:text>"</xsl:text>
<xsl:value-of select="@phone"/>
<xsl:text>",</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="@street1"/>
<xsl:text>",</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="normalize-space(ln/lnlnNm)"/>
<xsl:text>",</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="normalize-space(ln/lnFirstNm/lnfnNm)"/>
<xsl:text>",</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="airEnd">
<xsl:text>#10;</xsl:text>
</xsl:template>
</xsl:stylesheet>
|
|
|

September 1st, 2006, 06:58 AM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
I'm not sure whether to tackle this from your code or from your requirements. Your code is badly wrong, you clearly haven't understood how variables and parameters work: I'd suggest you do some more reading in this area. Global variables can be used anywhere in the stylesheet; local variables can be used only in the template where they are defined; parameters can be passed from a calling template to a called template using xsl:with-param and xsl:param.
From the requirements end, you haven't shown your actual XML; but it looks like a grouping problem, and to understand grouping you should read a good XSLT book and/or http://www.jenitennison.com/xslt/grouping.
Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
|
|

September 1st, 2006, 07:51 AM
|
|
Registered User
|
|
Join Date: Aug 2006
Posts: 5
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Well, I guess you can say this is my first attempt at XSL, and I thought I expressed my lack of knowledge when it came to using variables and parameters. Sorry about not posting the XML. Here is one record:
<airBegin>
<auditNbr>1391</auditNbr>
</airBegin>
<acctSect>
<reqId>
<ponId>B2006082523140800001886</ponId>
<ponVerId>B </ponVerId>
<compDt>05-31-2005</compDt>
<inSrcCd>B</inSrcCd>
<inLogId>000000</inLogId>
</reqId>
<actSrcCd>B</actSrcCd>
<uacctId>
<atnNbr>7194950000</atnNbr>
<ccCd>9636</ccCd>
</uacctId>
<reqAct>
<actCd>N</actCd>
</reqAct>
<inTosTxt>1S N</inTosTxt>
<miscAcct>
<mainReqdInd>Y</mainReqdInd>
<lspTypCd>D</lspTypCd>
</miscAcct>
<inSa>
<addrActCd>N</addrActCd>
<sa>
<astrt>
<ahn>
<ahnbrNbr>2032</ahnbrNbr>
</ahn>
<astrtNm>
<astrtNm>Silver Horn</astrtNm>
<athoroTxt>Ln</athoroTxt>
</astrtNm>
</astrt>
<aloc>
<locId>N5ICO</locId>
<alocNm>Monument</alocNm>
</aloc>
<astateCd>CO</astateCd>
<azipCd>80132-8091 </azipCd>
</sa>
</inSa>
</acctSect>
<lstgSect>
<lstgReq>
<aliCd>
</aliCd>
<lact>
<lactCd>N</lactCd>
</lact>
<actSrcCd>B</actSrcCd>
<lctl>
<rty>
<lstgScopeCd>L</lstgScopeCd>
<rtyCd>ML</rtyCd>
</rty>
<lstgTypCd>L</lstgTypCd>
<broCd>D</broCd>
<revDaccInd>N</revDaccInd>
<dspScopeCd>12986602</dspScopeCd>
<sicCd>005999</sicCd>
<langCd>E</langCd>
<pndgCd>N</pndgCd>
</lctl>
<lappCtl>
<stylCd>SL</stylCd>
<doiNbr>0</doiNbr>
<tyaCd>B</tyaCd>
</lappCtl>
<lstgMgmt>
<lnPlcmtCd>W</lnPlcmtCd>
<noslInd>N</noslInd>
</lstgMgmt>
<inProd>
<prodScopeCd>P</prodScopeCd>
<inProdIdTxt>
<inProdTxt>CS</inProdTxt>
</inProdIdTxt>
</inProd>
<ltnFlds>
<tn>
<countryCd>001</countryCd>
<areaCd>719</areaCd>
<coCd>481</coCd>
<lineNbr>9300</lineNbr>
</tn>
<omtnInd>N</omtnInd>
</ltnFlds>
<ln>
<dlnmInd>N</dlnmInd>
<lnAbbInd>Y</lnAbbInd>
<lnlnNm>Bill</lnlnNm>
<lnFirstNm>
<lnfnNm>Smith</lnfnNm>
</lnFirstNm>
</ln>
<dspData>
<dsp1Nbr>531</dsp1Nbr>
<dsp2Nbr>2005</dsp2Nbr>
</dspData>
</lstgReq>
</lstgSect>
<lstgSect>
<lstgReq>
<aliCd>A1 </aliCd>
<lact>
<lactCd>N</lactCd>
</lact>
<actSrcCd>B</actSrcCd>
<lctl>
<rty>
<lstgScopeCd>L</lstgScopeCd>
<rtyCd>AM</rtyCd>
</rty>
<lstgTypCd>L</lstgTypCd>
<broCd>D</broCd>
<revDaccInd>N</revDaccInd>
<dspScopeCd>12986603</dspScopeCd>
<sicCd>005999</sicCd>
<langCd>E</langCd>
<pndgCd>N</pndgCd>
</lctl>
<lappCtl>
<stylCd>SL</stylCd>
<doiNbr>0</doiNbr>
<tyaCd>B</tyaCd>
</lappCtl>
<lstgMgmt>
<lnPlcmtCd>W</lnPlcmtCd>
<noslInd>N</noslInd>
</lstgMgmt>
<inProd>
<prodScopeCd>P</prodScopeCd>
<inProdIdTxt>
<inProdTxt>CS</inProdTxt>
</inProdIdTxt>
</inProd>
<ltnFlds>
<tn>
<countryCd>001</countryCd>
<areaCd>719</areaCd>
<coCd>481</coCd>
<lineNbr>9300</lineNbr>
</tn>
<omtnInd>N</omtnInd>
</ltnFlds>
<ln>
<dlnmInd>N</dlnmInd>
<lnAbbInd>Y</lnAbbInd>
<lnlnNm>Sally</lnlnNm>
<lnFirstNm>
<lnfnNm>Smith</lnfnNm>
</lnFirstNm>
</ln>
<dspData>
<dsp1Nbr>531</dsp1Nbr>
<dsp2Nbr>2005</dsp2Nbr>
</dspData>
</lstgReq>
</lstgSect>
<lstgSect>
<lstgReq>
<aliCd>A2 </aliCd>
<lact>
<lactCd>N</lactCd>
</lact>
<actSrcCd>B</actSrcCd>
<lctl>
<rty>
<lstgScopeCd>L</lstgScopeCd>
<rtyCd>AL</rtyCd>
</rty>
<lstgTypCd>L</lstgTypCd>
<broCd>D</broCd>
<revDaccInd>N</revDaccInd>
<dspScopeCd>12986604</dspScopeCd>
<sicCd>005999</sicCd>
<langCd>E</langCd>
<pndgCd>N</pndgCd>
</lctl>
<lappCtl>
<stylCd>SL</stylCd>
<doiNbr>0</doiNbr>
<tyaCd>B</tyaCd>
</lappCtl>
<lstgMgmt>
<lnPlcmtCd>W</lnPlcmtCd>
<noslInd>N</noslInd>
</lstgMgmt>
<inProd>
<prodScopeCd>P</prodScopeCd>
<inProdIdTxt>
<inProdTxt>CS</inProdTxt>
</inProdIdTxt>
</inProd>
<ltnFlds>
<tn>
<countryCd>001</countryCd>
<areaCd>719</areaCd>
<coCd>481</coCd>
<lineNbr>9300</lineNbr>
</tn>
<omtnInd>N</omtnInd>
</ltnFlds>
<ln>
<dlnmInd>N</dlnmInd>
<lnAbbInd>Y</lnAbbInd>
<lnlnNm>Smith</lnlnNm>
<lnFirstNm>
<lnfnNm>John</lnfnNm>
</lnFirstNm>
</ln>
<dspData>
<dsp1Nbr>531</dsp1Nbr>
<dsp2Nbr>2005</dsp2Nbr>
</dspData>
</lstgReq>
</lstgSect>
<airEnd />
|
|

September 1st, 2006, 08:06 AM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
So could you rephrase the requirement in terms of your real data? I don't see any Smiths in there...
Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
|
|

September 1st, 2006, 08:17 AM
|
|
Registered User
|
|
Join Date: Aug 2006
Posts: 5
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Michael, I have modified my above post to reflect the "smiths". They are located within the <ln> tags with the <lnfnNm> and <lnlnnm>.
I'm not exactly sure what you mean by rephrasing the requirement but I can explain a little further. This list is given to us, with a ton of extra data we don't need. All I'm really trying to do is pull out the addresses and phone numbers -- one address with possibly multiple contacts. I have no control over the data, it comes as-is.
|
|

September 1st, 2006, 08:33 AM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
OK, I'm closer. I'm still not sure what the element holding the 1234 Anywhere Street is - your element names are far more cryptic than I can decipher - but it's not actually a grouping problem, rather it's the opposite, which is relatively straightforward. Logically, all you need is something like this:
<xsl:template match="lstgSect">
<xsl:value-of select="... path to the person's name ..."/>
<xsl:value-of select="preceding-sibling::acctSect[1]/ (...path to the address ...)
...
</xsl:template>
In other words, every time you hit a lstgSect, first navigate your way to the fields containing the name, then go back to the most recent acctSect and from there, navigate your way to the field holding the address. No need for variables here.
Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
|
|

September 1st, 2006, 09:27 AM
|
|
Registered User
|
|
Join Date: Aug 2006
Posts: 5
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Hi Michael, that is great. One question, if you look at the xml code again, the actual first name and last name is located in the <lstgReq>, which is inside the <lstgSect>.
Like this:
<lstgSect>
<lstgReq>
From your example above, if I change to <xsl:template match="lstgReq">, would the preceding-sibling still be acctSect? To me, it appears it would be one level removed, but then again, I'm a newbie.
|
|

September 1st, 2006, 10:03 AM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
I suggested
<xsl:template match="lstgSect">
<xsl:value-of select="... path to the person's name ..."/>
<xsl:value-of select="preceding-sibling::acctSect[1]/ (...path to the address ...)
...
</xsl:template>
because it's easy from lstgSect to find your way down to the person's name, and back to the address. You are right that if you started at a different point, the navigation would be different, for example you would need to do ../preceding-sibling::acctSect[1]/
Note that the two value-of instructions have the same context node - they both start navigating from the same point. Nothing you do in the first value-of affects the starting point (or the result) of the second.
Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
|
|

September 1st, 2006, 12:01 PM
|
|
Registered User
|
|
Join Date: Aug 2006
Posts: 5
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Hi Michael, that works great. I really appreciate your patience with a newbie.
One last question. this particular XML sheet has roughly 2,000 records in it. Using the preceding-sibling coding really makes it take a while to churn through these records. I also have another xml sheet just like this one with almost 19,000 records.
Is there anyway to optimize this so it goes faster?
Here's my new XSL sheet:
<?xml version="1.0"?>
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="Data">
<xsl:apply-templates select="lstgSect | airEnd"/>
</xsl:template>
<xsl:template match="lstgSect">
<xsl:for-each select="lstgReq">
<xsl:text>"</xsl:text>
<xsl:value-of select="../preceding-sibling::acctSect[1]/uacctId/atnNbr"/>
<xsl:text>",</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="../preceding-sibling::acctSect[1]/inSa/sa/astrt/ahn/ahnbrNbr"/>
<xsl:text>",</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="../preceding-sibling::acctSect[1]/inSa/sa/astrt/astrtNm/astrtNm"/>
<xsl:text>",</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="../preceding-sibling::acctSect[1]/inSa/sa//astrt/astrtNm/athoroTxt"/>
<xsl:text>",</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="../preceding-sibling::acctSect[1]/inSa/sa/aloc/alocNm"/>
<xsl:text>",</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="../preceding-sibling::acctSect[1]/inSa/sa/astateCd"/>
<xsl:text>",</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="../preceding-sibling::acctSect[1]/inSa/sa/azipCd"/>
<xsl:text>",</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="normalize-space(ln/lnlnNm)"/>
<xsl:text>",</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="normalize-space(ln/lnFirstNm/lnfnNm)"/>
<xsl:text>"</xsl:text>
<xsl:text>#10;</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="airEnd">
</xsl:template>
</xsl:stylesheet>
|
|

September 1st, 2006, 12:16 PM
|
 |
Wrox Author
|
|
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
|
|
Since you're only interested in a very small part of the data, a good approach would be to first run a sequential pass through the data that only copies over the elements you are interested in; the second pass can then do the more complex navigation on a smaller dataset. You can do a two-pass transformation using two stylesheets (my usual preference) or in a single stylesheet of the form:
<xsl:template match="/"
<xsl:variable name="p1">
<xsl:apply-templates mode="phase1"/>
</xsl:variable>
<xsl:apply-templates select="xx:node-set($p1)" mode="phase2"/>
</xsl:template>
where xx:node-set() is a vendor extension function (most products have one, but the name varies). In XSLT 2.0 you can just do select="$p1".
Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
|
|
 |