Reputation: 52
<Data>
<Record Id="1826155" username="BobDylan" >
<ART id="121028890624">
<TYPE>Song</TYPE>
<AUTH>
<AUTHOR_ID>1826155</AUTHOR_ID>
<FNAME>Bob</FNAME>
<LNAME>Dylan</LNAME>
<HIDE/>
</AUTH>
<AUTH>
<AUTHOR_ID>175782</AUTHOR_ID>
<FNAME>Frank</FNAME>
<LNAME>Sinatra</LNAME>
<HIDE>true</HIDE>
</AUTH>
<STATUS>Published</STATUS>
<TITLE>A Cool Song </TITLE>
</ART>
<ART id="123840712342">
<TYPE>Song</TYPE>
<AUTH>
<AUTHOR_ID>1826155</AUTHOR_ID>
<FNAME>Bob</FNAME>
<LNAME>Dylan</LNAME>
<HIDE>true</HIDE>
</AUTH>
<AUTH>
<AUTHOR_ID>175782</AUTHOR_ID>
<FNAME>Tom</FNAME>
<LNAME>Waits</LNAME>
</AUTH>
<STATUS>Published</STATUS>
<TITLE>New Song</TITLE>
</ART>
<ART id="123840737280">
<TYPE>Book</TYPE>
<AUTH>
<AUTHOR_ID>1826155</AUTHOR_ID>
<FNAME>Bob</FNAME>
<LNAME>Dylan</LNAME>
<HIDE/>
</AUTH>
<AUTH>
<AUTHOR_ID>175782</AUTHOR_ID>
<FNAME>Tom</FNAME>
<LNAME>Wolfe</LNAME>
</AUTH>
<STATUS>Accepted</STATUS>
<TITLE>New Book</TITLE>
</ART>
</Record>
</Data>
I'm trying to transform the above XML by selecting Song nodes that adhere to the following conditions:
The desired XML output should include the Type, all authors and the title. The second Song Type is hidden in this example (because HIDE = true for the main author):
<h3>Songs</h3>
<div>
<ul>
<li>Bob Dylan, Frank Sinatra; A Cool Song</li>
</ul>
</div>
I started out trying for-each, but found that I could not deal with displaying authors that did not match the Record Id. Based on suggestions, I believe this is best achieved using templates. I have some XSLT that is not working correctly, instead it's displaying all the data rather than the subset.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="html" encoding="utf-8" />
<xsl:variable name="userId" select="dm:Data/dm:Record/@userId"/>
<xsl:template match="/Data/Record/ART[TYPE='Song']">
<xsl:apply-templates select= "/Data/Record/ART[CONTYPE='Song']/AUTH[AUTHOR_ID = $userId]" />
</xsl:template>
<xsl:template match="/Data/Record/ART[TYPE='Song']">
<xsl:apply-templates select= "/Data/Record/ART[TYPE='Song']/AUTH[/STATUS = 'PUBLISHED']" />
</xsl:template>
<xsl:template match="/Data/Record/ART[TYPE='Song']">
<xsl:value-of select="./FNAME"/> <xsl:text> </xsl:text>
<xsl:value-of select="./LNAME"/> <xsl:text> </xsl:text>
<xsl:value-of select="./TITLE"/> <xsl:text> </xsl:text>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Views: 214
Reputation: 116993
I started out trying for-each, but found that I could not deal with displaying authors that did not match the Record Id.
Why not?
<xsl:for-each select="/Data/Record/ART/AUTH[AUTHOR_ID = ancestor::Record/@Id]">
will select only authors whose AUTHOR_ID matches the Id of their Record.
To narrow it down to authors of published songs only, you can use:
<xsl:for-each select="/Data/Record/ART[TYPE = 'Song' and STATUS = 'Published']/AUTH[AUTHOR_ID = ancestor::Record/@Id]">
To further exclude hidden authors:
<xsl:for-each select="/Data/Record/ART[TYPE = 'Song' and STATUS = 'Published']/AUTH[AUTHOR_ID = ancestor::Record/@Id and not(HIDE='true')]">
Of course, you could use the same predicates with xsl:apply-templates
, too.
in response to your edit:
i still find the requirements confusing. I think you want to do something like this:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/Data">
<h3>Songs</h3>
<div>
<xsl:for-each select="Record/ART[TYPE = 'Song' and STATUS = 'Published' and not(parent::Record/@Id = AUTH[HIDE='true']/AUTHOR_ID)]">
<ul>
<li>
<xsl:for-each select="AUTH">
<xsl:value-of select="FNAME"/>
<xsl:text> </xsl:text>
<xsl:value-of select="LNAME"/>
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>; </xsl:text>
<xsl:value-of select="TITLE"/>
</li>
</ul>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
This will select every ART that satisfies all of these conditions:
TYPE = 'Song';
STATUS = 'Published';
none of its authors has both (a) an AUTHOR_ID that matches the ancestor's Record Id and (b) HIDE = 'True'.
For every such ART, all of its authors will be listed, along with the the ART's title:
Result
<h3>Songs</h3><div>
<ul>
<li>Bob Dylan, Frank Sinatra; A Cool Song </li>
</ul>
</div>
Upvotes: 1
Reputation: 2645
This should work.
The first match finds the records and stores the id in the recId variable. It then uses this variable to apply the match on ART with that id and the other parameters you specified. The sample XML you provided had no namespaces, so I left them off.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="html" encoding="utf-8" />
<xsl:template match="/Data/Record">
<xsl:variable name="recId" select="@Id"/>
<xsl:apply-templates select="ART[TYPE='Song' and STATUS='Published' and AUTH/AUTHOR_ID=$recId and AUTH/HIDE!='true']" />
</xsl:template>
<xsl:template match="Data/Record/ART">
<xsl:value-of select="TITLE"/>
<xsl:value-of select="AUTH/FNAME"/>
<xsl:value-of select="AUTH/LNAME"/>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1