distinct values xslt using overriding param or Array - xslt

My xml looks like this:
<topics>
<topic name="topic1" />
<topic name="topic2" />
<topic name="topic3" />
<topic name="topic4" />
<topic name="topic5" />
<topic name="topic6" />
<topic name="topic7" />
</topics>
<supertopics>
<supertopic title="supertopic1" name="1;topic1;#2;#topic2;3;#topic3" />
<supertopic title="supertopic2" name="4;#topic4;#7;#topic7" />
<supertopic title="supertopic3" name="2;#topic2;#3;#topic3" />
<supertopic title="supertopic4" name="5;#topic5;#7;#topic7" />
<supertopic title="supertopic5" name="3;#topic3;#7;#topic7" />
<supertopic title="supertopic6" name="4;#topic4;#7;#topic7" />
<supertopic title="supertopic7" name="5;#topic5;#7;#topic7" />
<supertopic title="supertopic8" name="2;#topic2;#6;#topic6" />
<supertopic title="supertopic9" name="5;#topic6;#7;#topic7" />
<supertopic title="supertopic10" name="3;#topic3;#4;#topic4" />
</supertopics>
I basically want 1 supertopic per topic.So that means I have 7 topics and I want 7 most recent supertopics associated with it. I have date also with it through I m doing sorting but main thing is I want these 7 supertopics to be unique as there are multiple supertopics per topic.
So I want my output to be like this:
supertopic1 (topic1 is associated to supertopic1)
supertopic3 (topic2 is associated to supertopic1 but as its already there i want it to look for next supertopic its associated to)
supertopic5 (topic3)
supertopic2 (topic4)
supertopic4 (topic5)
supertopic8 (topic6)
supertopic6 (topic7)
I am using xsl 1.0
and I was trying to achieve it using but i couldn't find any way to do this:
<xsl:param name="FilteredAssets1">
<stopic></stopic>
<ttopic></ttopic>
</xsl:param>
<xsl:for-each select="topics/topic/#name">
<xsl:variable name="topicname">
<xsl:value-of select="."></xsl:value-of>
</xsl:variable>
<xsl:for-each select="/supertopics/supertopic[contains(#name,$topicname)]">
<xsl:if test="not(contains(msxsl:node-set($FilteredAssets1)/stopic,#title)) and not(contains(msxsl:node-set($FilteredAssets1)/ttopic,$programname))">
<stopic><xsl:value-of select="#title"></xsl:value-of></stopic>
<ttopic><xsl:value-of select="$programname"></xsl:value-of></ttopic>
</xsl:if>
</xsl:for-each>
</xsl:for-each>

This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vSupers"
select="/*/supertopics/supertopic"/>
<xsl:template match="/*">
<xsl:apply-templates select="topics/topic[1]"/>
</xsl:template>
<xsl:template match="topic">
<xsl:param name="pUsedSupers" select="/.."/>
<xsl:variable name="vNewSuper" select=
"$vSupers
[not(#title = $pUsedSupers/#title)
and
contains(concat(#name, ';'),
concat('#', current()/#name, ';')
)
]
[1]
"/>
<xsl:value-of select="concat('
', $vNewSuper/#title)"/>
<xsl:apply-templates select="following-sibling::topic[1]">
<xsl:with-param name="pUsedSupers"
select="$pUsedSupers | $vNewSuper"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML text (wrapped in with a single top element to be made a well-formed XML document and also with acorrection to what I think is a typo for supertopic1):
<t>
<topics>
<topic name="topic1" />
<topic name="topic2" />
<topic name="topic3" />
<topic name="topic4" />
<topic name="topic5" />
<topic name="topic6" />
<topic name="topic7" />
</topics>
<supertopics>
<supertopic title="supertopic1" name="1;#topic1;#2;#topic2;3;#topic3" />
<supertopic title="supertopic2" name="4;#topic4;#7;#topic7" />
<supertopic title="supertopic3" name="2;#topic2;#3;#topic3" />
<supertopic title="supertopic4" name="5;#topic5;#7;#topic7" />
<supertopic title="supertopic5" name="3;#topic3;#7;#topic7" />
<supertopic title="supertopic6" name="4;#topic4;#7;#topic7" />
<supertopic title="supertopic7" name="5;#topic5;#7;#topic7" />
<supertopic title="supertopic8" name="2;#topic2;#6;#topic6" />
<supertopic title="supertopic9" name="5;#topic6;#7;#topic7" />
<supertopic title="supertopic10" name="3;#topic3;#4;#topic4" />
</supertopics>
</t>
produces the wanted, correct result:
supertopic1
supertopic3
supertopic5
supertopic2
supertopic4
supertopic8
supertopic6

Related

xslt code requirement [duplicate]

Below is input XML code and I want to replace wherever this __ character sequence is mentioned it to be replaced by a colon : character.
For example, if it is sbdh__sender, it should be replace by sbdh:sender.
<?xml version="1.0" encoding="UTF-8"?>
<ns1:EPCISDocument xmlns:ns1="http://apse.com" schemaVersion="" creationDate="">
<EPCISHeader>
<sbdh__StandardBusinessDocumentHeader>
<sbdh__HeaderVersion/>
<sbdh__Sender>
<sbdh__Identifier Authority=""/>
<sbdh__ContactInformation>
<sbdh__Contact/>
<sbdh__EmailAddress/>
<sbdh__FaxNumber/>
<sbdh__TelephoneNumber/>
<sbdh__ContactTypeIdentifier/>
</sbdh__ContactInformation>
</sbdh__Sender>
<sbdh__Receiver>
<sbdh__Identifier Authority=""/>
<sbdh__ContactInformation>
<sbdh__Contact/>
<sbdh__EmailAddress/>
<sbdh__FaxNumber/>
<sbdh__TelephoneNumber/>
<sbdh__ContactTypeIdentifier/>
</sbdh__ContactInformation>
</sbdh__Receiver>
<sbdh__Manifest>
<sbdh__NumberOfItems/>
<sbdh__ManifestItem>
<sbdh__MimeTypeQualifierCode/>
<sbdh__UniformResourceIdentifier/>
<sbdh__Description/>
<sbdh__LanguageCode/>
</sbdh__ManifestItem>
</sbdh__Manifest>
<sbdh__BusinessScope>
<sbdh__Scope>
<sbdh__BusinessService>
<sbdh__BusinessServiceName/>
<sbdh__ServiceTransaction TypeOfServiceTransaction="" IsNonRepudiationRequired="" IsAuthenticationRequired="" IsNonRepudiationOfReceiptRequired="" IsIntegrityCheckRequired="" IsApplicationErrorResponseRequired="" TimeToAcknowledgeReceipt="" TimeToAcknowledgeAcceptance="" TimeToPerform=""/>
</sbdh__BusinessService>
<sbdh__CorrelationInformation>
<sbdh__RequestingDocumentCreationDateTime/>
<sbdh__RequestingDocumentInstanceIdentifier/>
<sbdh__ExpectedResponseDateTime/>
</sbdh__CorrelationInformation>
</sbdh__Scope>
</sbdh__BusinessScope>
</sbdh__StandardBusinessDocumentHeader>
</EPCISHeader>
<EPCISBody>
<EventList>
<ObjectEvent>
<eventTime/>
<recordTime/>
<eventTimeZoneOffset/>
<epcList>
<epc type=""/>
</epcList>
<action/>
<bizStep/>
<disposition/>
<readPoint>
<id/>
</readPoint>
<bizLocation>
<id/>
</bizLocation>
<bizTransactionList>
<bizTransaction type=""/>
</bizTransactionList>
<gsk__GskEpcExtension>
<gsk__manufacturingDate>1234</gsk__manufacturingDate>
</gsk__GskEpcExtension>
</ObjectEvent>
</EventList>
</EPCISBody>
</ns1:EPCISDocument>
Any help on this will be appreciated.
XSLT ...
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="http://apse.com">
<xsl:output method="xml" indent="yes" />
<!-- replace root element to add new namespaces -->
<xsl:template match="ns1:EPCISDocument">
<ns1:EPCISDocument
xmlns:gsk="GSK Namespace Here"
xmlns:sbdh="SBDH Namespace Here"
>
<xsl:apply-templates select="#* | node()"/>
</ns1:EPCISDocument>
</xsl:template>
<!-- if item name has '__' then split it into QName with prefix:name -->
<xsl:template match="#* | node()[substring-after(local-name(), '__')]">
<xsl:variable name="prefix" select="substring-before(local-name(), '__')" />
<xsl:variable name="name" select="substring-after(local-name(), '__')" />
<xsl:variable name="namespace">
<xsl:choose>
<xsl:when test="$prefix = 'gsk'">GSK Namespace Here</xsl:when>
<xsl:when test="$prefix = 'sbdh'">SBDH Namespace Here</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:element name="{$prefix}:{$name}" namespace="{$namespace}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<!-- otherwise just copy the item -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
... Output ...
<?xml version="1.0" encoding="utf-8"?>
<ns1:EPCISDocument schemaVersion="" creationDate="" xmlns:ns1="http://apse.com" xmlns:gsk="GSK Namespace Here" xmlns:sbdh="SBDH Namespace Here">
<EPCISHeader>
<sbdh:StandardBusinessDocumentHeader>
<sbdh:HeaderVersion />
<sbdh:Sender>
<sbdh:Identifier Authority="" />
<sbdh:ContactInformation>
<sbdh:Contact />
<sbdh:EmailAddress />
<sbdh:FaxNumber />
<sbdh:TelephoneNumber />
<sbdh:ContactTypeIdentifier />
</sbdh:ContactInformation>
</sbdh:Sender>
<sbdh:Receiver>
<sbdh:Identifier Authority="" />
<sbdh:ContactInformation>
<sbdh:Contact />
<sbdh:EmailAddress />
<sbdh:FaxNumber />
<sbdh:TelephoneNumber />
<sbdh:ContactTypeIdentifier />
</sbdh:ContactInformation>
</sbdh:Receiver>
<sbdh:Manifest>
<sbdh:NumberOfItems />
<sbdh:ManifestItem>
<sbdh:MimeTypeQualifierCode />
<sbdh:UniformResourceIdentifier />
<sbdh:Description />
<sbdh:LanguageCode />
</sbdh:ManifestItem>
</sbdh:Manifest>
<sbdh:BusinessScope>
<sbdh:Scope>
<sbdh:BusinessService>
<sbdh:BusinessServiceName />
<sbdh:ServiceTransaction TypeOfServiceTransaction="" IsNonRepudiationRequired="" IsAuthenticationRequired="" IsNonRepudiationOfReceiptRequired="" IsIntegrityCheckRequired="" IsApplicationErrorResponseRequired="" TimeToAcknowledgeReceipt="" TimeToAcknowledgeAcceptance="" TimeToPerform="" />
</sbdh:BusinessService>
<sbdh:CorrelationInformation>
<sbdh:RequestingDocumentCreationDateTime />
<sbdh:RequestingDocumentInstanceIdentifier />
<sbdh:ExpectedResponseDateTime />
</sbdh:CorrelationInformation>
</sbdh:Scope>
</sbdh:BusinessScope>
</sbdh:StandardBusinessDocumentHeader>
</EPCISHeader>
<EPCISBody>
<EventList>
<ObjectEvent>
<eventTime />
<recordTime />
<eventTimeZoneOffset />
<epcList>
<epc type="" />
</epcList>
<action />
<bizStep />
<disposition />
<readPoint>
<id />
</readPoint>
<bizLocation>
<id />
</bizLocation>
<bizTransactionList>
<bizTransaction type="" />
</bizTransactionList>
<gsk:GskEpcExtension>
<gsk:manufacturingDate>1234</gsk:manufacturingDate>
</gsk:GskEpcExtension>
</ObjectEvent>
</EventList>
</EPCISBody>
</ns1:EPCISDocument>

How to get max valued xref's 'rid' except from particular section

Please suggest for how get the maximum 'rid' value from all xrefs except from the 'Online' sections. By identify the max valued 'rid', then need to insert the attribute to those references which are higher to maximum value. Please see required result text.
XML:
<article>
<body>
<sec><title>Sections</title>
<p>The test <xref rid="b1">1</xref>, <xref rid="b2">2</xref>, <xref rid="b3 b4 b5">3-5</xref></p></sec>
<sec><title>Online</title><!--This section's xrefs no need to consider-->
<p>The test <xref rid="b6">6</xref></p>
<sec><title>Other</title>
<p><xref rid="b1">1</xref>, <xref rid="b7 b8">7-8</xref></p>
</sec>
</sec><!--This section's xrefs no need to consider-->
<sec>
<p>Final test test</p>
<sec><title>Third title</title><p>Last text</p></sec>
</sec>
</body>
<bm>
<ref id="b1">The ref01</ref>
<ref id="b2">The ref02</ref>
<ref id="b3">The ref03</ref>
<ref id="b4">The ref04</ref>
<ref id="b5">The ref05</ref>
<ref id="b6">The ref06</ref>
<ref id="b7">The ref07</ref>
<ref id="b8">The ref08</ref>
</bm>
</article>
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:variable name="var1"><!--Variable to get the all 'rid's except sec/title contains 'Online' -->
<xsl:for-each select="//xref[not(. is ancestor::sec[title[contains(., 'Online')]]/descendant-or-self)]/#rid">
<!--xsl:for-each select="//xref/#rid[not(contains(ancestor::sec/title, 'Online'))]"--><!--for this xpath, error is : "XPTY0004: A sequence of more than one item is not allowed as the first argument" -->
<!--xsl:for-each select="//xref/#rid[not(contains(ancestor::sec[1]/title, 'Online')) and not(contains(ancestor::sec[2]/title, 'Online'))]"--><!--for this xpath we are getting the required result, but there may be several nesting of 'sec's -->
<xsl:choose>
<xsl:when test="contains(., ' ')">
<xsl:for-each select="tokenize(., ' ')">
<a><xsl:value-of select="."/></a>
</xsl:for-each>
</xsl:when>
<xsl:otherwise><a><xsl:value-of select="."/></a></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="varMax1">
<xsl:for-each select="$var1/a">
<xsl:sort select="substring-after(., 'b')" order="descending" data-type="number"/>
<a><xsl:value-of select="."/></a>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="varMax"><!--Variable to get max valued RID -->
<xsl:value-of select="substring-after($varMax1/a[1], 'b')"/>
</xsl:variable>
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
<xsl:template match="ref">
<xsl:variable name="varID"><xsl:value-of select="substring-after(#id, 'b')"/></xsl:variable>
<xsl:choose>
<xsl:when test="number($varMax) lt number($varID)">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="MoveRef">yes</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Required result:
<article>
<body>
<sec><title>Sections</title>
<p>The test <xref rid="b1">1</xref>, <xref rid="b2">2</xref>, <xref rid="b3 b4 b5">3-5</xref></p></sec>
<sec><title>Online</title><!--This section's xrefs no need to consider-->
<p>The test <xref rid="b6">6</xref></p>
<sec><title>Other</title>
<p><xref rid="b1">1</xref>, <xref rid="b7">7</xref>, <xref rid="b8">8</xref></p>
</sec>
</sec><!--This section's xrefs no need to consider-->
<sec>
<p>Final test test</p>
<sec><title>Third title</title><p>Last text</p></sec>
</sec>
</body>
<bm>
<ref id="b1">The ref01</ref>
<ref id="b2">The ref02</ref>
<ref id="b3">The ref03</ref>
<ref id="b4">The ref04</ref>
<ref id="b5">The ref05</ref>
<ref id="b6" MoveRef="yes">The ref06</ref>
<ref id="b7" MoveRef="yes">The ref07</ref>
<ref id="b8" MoveRef="yes">The ref08</ref>
</bm>
</article>
Here consider number 5 for 'b5' rid, 6 for 'b6'.... (Because alphanumeric)
Perhaps you can take a different approach rather than trying to find the maximum rid attribute that is not in an "online" section. Not least because it is not entirely clear what the maximum is when you are dealing with an alphanumeric string.
Instead, you could define a key to look up elements in the "online" section by their name
<xsl:key name="online" match="sec[title = 'Online']//*" use="name()" />
And then, another key, to look up the xref elements that occur in other sections
<xsl:key name="other" match="xref[not(ancestor::sec/title = 'Online')]" use="name()" />
Then, you can write a template to math the ref elements, and use an xsl:if to determine whether to add MoveRef attribute to it:
<xsl:variable name="id" select="#id" />
<xsl:if test="key('online', 'xref')[tokenize(#rid, ' ')[. = $id]] and not(key('other', 'xref')[tokenize(#rid, ' ')[. = $id]])">
Try this much shorter XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="online" match="sec[title = 'Online']//*" use="name()" />
<xsl:key name="other" match="xref[not(ancestor::sec/title = 'Online')]" use="name()" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ref">
<ref>
<xsl:variable name="id" select="#id" />
<xsl:if test="key('online', 'xref')[tokenize(#rid, ' ')[. = $id]] and not(key('other', 'xref')[tokenize(#rid, ' ')[. = $id]])">
<xsl:attribute name="MoveRef" select="'Yes'" />
</xsl:if>
<xsl:apply-templates select="#*|node()"/>
</ref>
</xsl:template>
</xsl:stylesheet>
You can actually amend the ref template to put the condition in the template match, if you wanted...
<xsl:template match="ref[key('online', 'xref')[tokenize(#rid, ' ')[. = current()/#id]] and not(key('other', 'xref')[tokenize(#rid, ' ')[. = current()/#id]])]">
<ref MoveRef="Yes">
<xsl:apply-templates select="#*|node()"/>
</ref>
</xsl:template>

Namespace issue in xslt

I am trying to convert following xml into other xml but I am not getting the values for xCoordinate and yCoordinate. I would like to convert the structure from source - XML to Target-XML where the goocodes will match and the result would be assigned to x and y.
Source - XML
<?xml version="1.0"?>
<AddressResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" errorCode="0" errorDescription="">
<wrappedResultList xmlns="http://xlocate.xserver.ptvag.com">
<ResultAddress city="Amsterdam" city2="" country="NL" houseNumber="" postCode="1***" state="Noord-Holland" street="" adminRegion="Amsterdam" appendix="" classificationDescription="EXACT" countryCapital="Amsterdam" detailLevelDescription="CITY" totalScore="100">
<wrappedAdditionalFields />
<coordinates>
<kml xsi:nil="true" xmlns="http://common.xserver.ptvag.com" />
<point x="4.89327999999999" y="52.373090000000005" xmlns="http://common.xserver.ptvag.com" />
</coordinates>
</ResultAddress>
<ResultAddress city="Amsterdam-Zuidoost" city2="" country="NL" houseNumber="" postCode="110*" state="Noord-Holland" street="" adminRegion="Amsterdam" appendix="" classificationDescription="EXACT" countryCapital="Amsterdam" detailLevelDescription="CITY" totalScore="80">
<wrappedAdditionalFields />
<coordinates>
<kml xsi:nil="true" xmlns="http://common.xserver.ptvag.com" />
<point x="4.9513699999999838" y="52.316199999999988" xmlns="http://common.xserver.ptvag.com" />
</coordinates>
</ResultAddress>
<ResultAddress city="Nieuw-Amsterdam" city2="" country="NL" houseNumber="" postCode="7833" state="Drenthe" street="" adminRegion="Emmen" appendix="" classificationDescription="EXACT" countryCapital="Amsterdam" detailLevelDescription="CITY" totalScore="80">
<wrappedAdditionalFields />
<coordinates>
<kml xsi:nil="true" xmlns="http://common.xserver.ptvag.com" />
<point x="6.8528699999999994" y="52.716139999999982" xmlns="http://common.xserver.ptvag.com" />
</coordinates>
</ResultAddress>
</wrappedResultList>
</AddressResponse>
Target - XML
<GeoCodeResponse>
<geocordinate>
<xCordinate>4.89327999999999</xCordinate>
<yCordinate>52.716139999999982</yCordinate>
</geocordinate>
</GeoCodeResponse>
XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xl="http://xlocate.xserver.ptvag.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance" xmlns:cm="http://common.xserver.ptvag.com" exclude-result-prefixes="xl xsi xsd cm" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<GeoCodeResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<geocordinate xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<xsl:for-each select="AddressResponse/xl:wrappedResultList/xl:ResultAddress">
<xsl:sort select="#xl:totalScore" order="descending" data-type="number"/>
<xsl:if test="position()= 1">
<xCordinate> <xsl:value-of select="/xl:coordinates/cm:point/cm:x" /></xCordinate>
<yCordinate> <xsl:value-of select="/xl:coordinates/cm:point/cm:y" /></yCordinate>
</xsl:if>
</xsl:for-each>
</geocordinate>
</GeoCodeResponse>
</xsl:template>
</xsl:stylesheet>
Please help what could be done in above xslt.
You were almost there. The coordinates are attributes, not nodes.
change it into this:
<xsl:if test="position()= 1">
<xCordinate>
<xsl:value-of select="xl:coordinates/cm:point/#x" />
</xCordinate>
<yCordinate>
<xsl:value-of select="xl:coordinates/cm:point/#y" />
</yCordinate>
</xsl:if>
You are also referencing the coordinates node from the root, but it should be relative. I changed /xl:coordinaties into xl:coordinates

Use XSLT key() function to lookup nodes based on two attributes in separate elements

I'm trying to use the XSLT key() function to return all the <Code> elements in an XML file that match the following two criteria:
Code[code=$code] AND ancestor::CodeType[type=$codeType]`
Here is a simplified example of what the input XML looks like:
<Items>
<Item code-number="C1" category="ABC" />
<Item code-number="C3" category="ABC" />
<Item code-number="C1" category="XYZ" />
</Items>
<CodeTypes>
<CodeType type="ABC">
<SubType title="Category III Codes"> <!-- <SubType> elements are optional -->
<SubType title="Subcategory III-15 Codes">
<Code code="C1" description="Red" />
<Code code="C2" description="Green" />
<Code code="C3" description="Blue" />
<Code code="C3" description="Purple" /> <!-- Same code can appear more than once -->
</SubType>
</SubType>
<CodeType>
<CodeType type="XYZ">
<Code code="C1" description="Black" /> <!-- Same code can be used for multiple CodeTypes -->
<Code code="C2" description="Orange" />
<Code code="C3" description="Yellow" />
<CodeType>
</CodeTypes>
Note that the comments aren't actually there in the actual XML, I'm just adding them here to clarify the XML structure.
Here is the XSLT transform I am trying to use, though it doesn't seem to be working:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="LookupMatchingCodeElements" match="Code" use="concat(../#code, '+', ancestor::CodeType/#type)" />
<xsl:template match="Item">
<xsl:call-template name="GetCodeElements">
<xsl:with-param name="code" select="#code-number" />
<xsl:with-param name="codeType" select="#category" />
</xsl:call-template>
</xsl:template>
<xsl:template name="GetCodeElements">
<xsl:param name="code" />
<xsl:param name="codeType" />
<xsl:for-each select="key('LookupMatchingCodeElements', concat($code, '+', $codeType))">
<!-- process each <Code> element -->
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
And this is what I want the key() function to return with different inputs:
<!-- For code="C1" AND codeType="ABC" -->
<Code code="C1" description="Red" />
<!-- For code="C3" AND codeType="ABC" -->
<Code code="C3" description="Blue" />
<Code code="C3" description="Purple" />
<!-- For code="C1" AND codeType="XYZ" -->
<Code code="C1" description="Black" />
Is this possible with the key() function? As there are hundreds of thousands of both <Item> and <Code> elements, being able to use <xsl:key> is very important.
Use:
<xsl:key name="kCode" match="Code"
use="concat(ancestor::CodeType[1]/#type, '+', #code)"/>
Here is a complete transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kCode" match="Code"
use="concat(ancestor::CodeType[1]/#type, '+', #code)"/>
<xsl:template match="/">
<xsl:copy-of select="key('kCode', 'ABC+C1')"/>
====================
<xsl:copy-of select="key('kCode', 'ABC+C3')"/>
====================
<xsl:copy-of select="key('kCode', 'XYZ+C1')"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following document (the provided XML text -- corrected malformedness):
<t>
<Items>
<Item code-number="C1" category="ABC" />
<Item code-number="C3" category="ABC" />
<Item code-number="C1" category="XYZ" />
</Items>
<CodeTypes>
<CodeType type="ABC">
<SubType title="Category III Codes">
<!-- <SubType> elements are optional -->
<SubType title="Subcategory III-15 Codes">
<Code code="C1" description="Red" />
<Code code="C2" description="Green" />
<Code code="C3" description="Blue" />
<Code code="C3" description="Purple" />
<!-- Same code can appear more than once -->
</SubType>
</SubType>
</CodeType>
<CodeType type="XYZ">
<Code code="C1" description="Black" />
<!-- Same code can be used for multiple CodeTypes -->
<Code code="C2" description="Orange" />
<Code code="C3" description="Yellow" />
</CodeType>
</CodeTypes>
</t>
the wanted, correct result is produced:
<Code code="C1" description="Red"/>
====================
<Code code="C3" description="Blue"/>
<Code code="C3" description="Purple"/>
====================
<Code code="C1" description="Black"/>

How can I generate basic documentation from a Relax NG Schema

I'm trying to generate very simple documentation from the annotations in a Relax NG XML Schema. For example, given the following Relax NG:
<?xml version="1.0" encoding="UTF-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0" xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<start>
<element name="topNode">
<ref name="topNode-ref"/>
</element>
</start>
<define name="topNode-ref">
<a:documentation>This is the top of the doc.</a:documentation>
<oneOrMore>
<element name="level1">
<ref name="level1-ref"/>
</element>
</oneOrMore>
</define>
<define name="level1-ref">
<a:documentation>Here's some notes about level1.</a:documentation>
<attribute name="att1">
<a:documentation>Details about att1.</a:documentation>
</attribute>
<element name="subLevel2">
<ref name="subLevel2-ref"/>
</element>
</define>
<define name="subLevel2-ref">
<a:documentation>Notes on subLevel2.</a:documentation>
<attribute name="subAtt"/>
<zeroOrMore>
<element name="subLevel3">
<ref name="subLevel3-ref"/>
</element>
</zeroOrMore>
</define>
<define name="subLevel3-ref">
<a:documentation>And here is subLevel3.</a:documentation>
<attribute name="subSubAtt"/>
</define>
</grammar>
Which would be used to valid an XML file like:
<?xml version="1.0" encoding="UTF-8"?>
<topNode>
<level1 att1="some test">
<subLevel2 subAtt="more text"></subLevel2>
</level1>
<level1 att1="quick">
<subLevel2 subAtt="brown">
<subLevel3 subSubAtt="fox"></subLevel3>
</subLevel2>
</level1>
</topNode>
I'd like to be able to produce documentation that lists the basic XPath to each element/attribute and then display any corresponding documentation annotations. For example:
/topNode
This is the top of the doc.
/topNode/level1
Here's some notes about level1
/topNode/level1/#att1
Details about att1.
etc...
Eventually, I'll add in more documentation about "zeroOrMore", possible data types, etc... but I need to get this first step solved first.
I've found the Techquila RELAX-NG Documentation Tools. I've played around with the rng to docbook stylesheet, but it don't do what I'm looking for. It just lists elements individually with no details about the XPath as far as I can tell. I don't see how I can use it as a starting point to get the output I'm after.
Is it possible (and if so, how?) to produce this type of documentation output with XSLT given the RelaxNG example provided?
While XSLT would be ideal, it's not a requirement. I'm open for anything that gets the job done.
This will work for a very simple grammar like your example.
<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:r="http://relaxng.org/ns/structure/1.0"
xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0"
>
<xsl:output method="text" />
<xsl:template match="/">
<xsl:apply-templates select="//r:define[a:documentation] | //r:attribute[a:documentation]" />
</xsl:template>
<xsl:template match="r:define">
<xsl:variable name="doc" select="a:documentation" />
<xsl:call-template name="print-path">
<xsl:with-param name="elm" select="//r:element[r:ref/#name=current()/#name]" />
</xsl:call-template>
<xsl:value-of select="$doc" /><xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="r:attribute">
<xsl:variable name="doc" select="a:documentation" />
<xsl:call-template name="print-path">
<xsl:with-param name="elm" select="//r:element[r:ref/#name=current()/ancestor::r:define/#name]" />
<xsl:with-param name="path" select="concat('/#',#name)" />
</xsl:call-template>
<xsl:value-of select="$doc" /><xsl:text>
</xsl:text>
</xsl:template>
<xsl:template name="print-path">
<xsl:param name="elm" />
<xsl:param name="path" />
<xsl:variable name="parent" select="//r:ref[#name=$elm/ancestor::r:define/#name]/ancestor::r:element" />
<xsl:message><xsl:value-of select="$elm/#name" /></xsl:message>
<xsl:choose>
<xsl:when test="$parent">
<xsl:call-template name="print-path">
<xsl:with-param name="elm" select="$parent" />
<xsl:with-param name="path" select="concat('/',$elm/#name,$path)" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('/',$elm/#name,$path)" /><xsl:text>
</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>