简体   繁体   中英

Call Template with Parameter and Output Value - XSLT

I have an 1 large xml file composed of 2 different xml files. What I want to do is, when I create a CSV file, I want to be able to grab data from the second part of the file that matches the first part. I use a for each loop to go through all of the data in the first portion of the file, and then a nested loop to grab matching data. Here is the XML layout:

<?xml version="1.0" encoding="UTF-8"?>
<Report_Data>
    <Report_Entry>
        <company_id>001</company_id>
        <dept>TestDept</dept>
    </Report_Entry>
    <Report_Entry>
    </Report_Entry>
    <Company_Data>
        <Report_Entry>
            <code>1</code>
            <name>Test1</name>
        </Report_Entry>
        <Report_Entry>
            <code>2</code>
            <name>Test2</name>
        </Report_Entry>
    </Company_Data>
</Report_Data>

What I would like in my CSV is to have the name from the Company_Data node inserted into the output right after the company_id.

Here is my XSL File (it currently does not work, it loops through and outputs N/A for each company data node in the output file):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wd="urn:com.workday/bsvc">
<xsl:output method="text" encoding="utf-8" media-type="text/plain" />
<xsl:strip-space  elements="*"/>

<xsl:variable name="newline" select="'&#x0A;'" />
<xsl:variable name="tab" select="'&#x09;'" />
<xsl:variable name="comma" select="'&#x2C;'" />
<xsl:variable name="padding" select="'                              '" />

<xsl:variable name="ID" select="concat('COMPANY ID', $padding)" />
<xsl:variable name="NAME" select="concat('COMPANY NAME', $padding)" />
<xsl:variable name="DEPT" select="concat('DEPARTMENT', $padding)" />

<xsl:template match="/">
    <xsl:value-of select="$ID"/><xsl:value-of select="$comma"/>
    <xsl:value-of select="$NAME"/><xsl:value-of select="$comma"/>
    <xsl:value-of select="$DEPT"/><xsl:value-of select="$comma"/>

    <xsl:for-each select="/Report_Data/Report_Entry">
    <xsl:value-of select="translate(substring(concat(company_id, $padding), 1, string-length($ID)), ',', ' ')" /><xsl:value-of select="$comma"/>

    <xsl:call-template name="company_name">
        <xsl:with-param name="id_number" select="company_id"></xsl:with-param>
</xsl:call-template>

    <xsl:value-of select="translate(substring(concat(dept, $padding), 1, string-length($DEPT)), ',', ' ')" /><xsl:value-of select="$comma"/>
</xsl:for-each>

</xsl:template>

<xsl:template name="company_name" match="/wd:Report_Data/wd:Company_Data">
<xsl:param name="id_number"></xsl:param>
<xsl:for-each select="/wd:Report_Entry">
    <xsl:if test="contains($id_number, code)">
        <xsl:value-of select="/name/text()"/>
    </xsl:if>
</xsl:for-each>
</xsl:template>

</xsl:stylesheet>

My output looks like this:

1, TestDept

Instead of:

1, Test1, TestDept

It is not outputting the value of the name node. How can I get it to display the value?

I can add stuff if more info is needed.

I cannot add a comment to ask for clarification on what you are expecting to output exactly, but some observations:

Currently your stylesheet will go through each company_id element, and for each one it finds it will loop through every Company_Data/Report_Entry element. You are seeing N/A because for each company_id it checks every single Company_Data/Report_Entry, and is outputting N/A for any that don't match the current company_id. To me it looks like you just need to remove the otherwise (in which case you wouldn't need a choose, just an if). Note that when I tried running your stylesheet I was getting 001,Test1,N/A,,Test1,N/A, so slightly different to yours (but I had to add a missing for-each closing tag).

In terms of the code and name elements, because you are in the for loop the focus has shifted to the element being looped on (Report_Data/Company_Data/Report_Entry) so you can use relative paths which will navigate from that looped element. So you can just use "code" or "name". You don't even need the "./" as this produces the same result.

You don't need to use an inner xsl:for-each loop here to look up the company name. You could just use an xsl:variable here with a select parameter where you can put your 'contains' expressions

<xsl:for-each select="Report_Data/Report_Entry">
    <xsl:variable 
         name="company_name" 
         select="/Report_Data/Company_Data/Report_Entry[contains(current()/company_id, code)]/name" />

Note the use of current() here, which is to ensure the expression uses the company_id of the current Report_Data/Report_Entry element you are on, as opposed to the Company_Data/Report_Entry you are searching in the expression. If you were to do what you were currently doing, namely /Report_Data/Report_Entry/company_id , then this is an 'absolute' expression which starts from the top-level document node and will always return the first company_id in the XML.

But in anycase, you could then simply output this variable, or use an xsl:choose to output 'NA' instead should the variable be empty (ie No company name was found).

Were it not for the fact the ids do not match up, such looking up of values are usually better handled with a key. To look up an Report_Entry by its code you would define a key like so

<xsl:key name="company" match="Company_Data/Report_Entry" use="code" />

Then to look it up you could do something like this...

<xsl:for-each select="Report_Data/Report_Entry">
    <xsl:variable name="company_name" select="key('company', company_id)" />

Of course, this would fail in your case because the ids are not exactly the same, but perhaps you can just remove leading zeroes. Try this, perhaps.

<xsl:for-each select="Report_Data/Report_Entry">
    <xsl:variable name="company_name" select="key('company', number(company_id))" />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM