Matching <hx id="label"> - xslt

I have source like
<h1> ... <h1>
(h1 may be h2 and h3 too.)
h1 MAY have an id attribute.
If it does NOT have an "id" attribute, I want to give it one.
How would the template look, to match an h1 WITHOUT and attribute "id", and how would I process the tag then?
Maybe like this?:
<xsl:template match="(h1 | h2 | h3)[not(#id)]" >
<!-- here I have to generate a tag h1, h2 or h3: how? -->
<xsl:attribute name="id">G10.2</xsl:attribute>
</h123>
</xsl:template>

How about:
<xsl:template match="h1[not(#id)] | h2[not(#id)] | h3[not(#id)]" >
<xsl:copy>
<xsl:attribute name="id">G10.2</xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>

Related

Make multiple changes with XLST 1.0 (uppercase element first letter, order elements, aggrate/group elements)

My input xml looks something like this:
<myFamily>
<spouse type="1">Halle Berry</spouse>
<parent type="bio">Jane Smith-Doe</parent>
<spouse type="2">Eva Longoria</spouse>
<uncle type="paternal">Bob Beam</uncle>
<parent type="bio">Jim Beam</parent>
<uncle type="maternal">Mike Smith</uncle>
<aunt type="paternal">Viola Davis</aunt>
<inLaw type="mother">Dr. Curry-Pepper</inLaw>
<brother name="Ron Isley">
<child>Sara Isley</child>
<child>Ron Isley Jr.</child>
<child>Martha Isley-Focker</child>
</brother>
<parent type="step">Jon Doe</parent>
<inLaw type="father">Dr. Pepper</inLaw>
<spouse type="3">Sofia Vergara</spouse>
<uncle type="paternal">Bo Beam</uncle>
<spouse type="3">Sonya Curry</spouse>
<Sister name ="Shelly Isley"/>
</myFamily>
I want it to end up like this:
<MyFamily>
<Parents>
<Parent type="bio">Jane Smith-Doe</Parent>
<Parent type="bio">Jim Beam</Parent>
<Parent type="step">Jon Doe</Parent>
</Parents>
<Siblings>
<Sister name ="Shelly Isley"/>
<Brother name="Ron Isley">
<Child>Sara Isley</Child>
<Child>Ron Isley Jr.</Child>
<Child>Martha Isley-Focker</Child>
</Brother>
<Siblings>
<Uncles>
<Uncle type="paternal">Bob Beam</Uncle>
<Uncle type="maternal">Mike Smith</Uncle>
<Uncle type="paternal">Bo Beam</Uncle>
</Uncles>
<Aunts><Aunt type="paternal">Viola Davis</Aunt><Aunts>
<InLaws>
<InLaw type="mother">Dr. Curry-Pepper</InLaw>
<InLaw type="father">Dr. Pepper</InLaw>
</InLaws>
<Wives>
<Wife type="1">Halle Berry</Wife>
<Wife type="2">Eva Longoria</Wife>
<Wife type="3">Sofia Vergara</Wife>
<Wife type="3">Sonya Curry</Wife>
</Wives>
</MyFamily>
To make the first letter uppercase, rename the spouse, and have it ordered a certain way I tried this and it didn't work:
<xsl:template match="#*|node()">
<xsl:copy>
<!-- Order Section Nodes -->
<xsl:apply-templates select="myFamily[(SectionName = 'parent')]" mode="nodeCopy"/>
<xsl:apply-templates select="myFamily[(SectionName = 'sister')]" mode="nodeCopy"/>
<xsl:apply-templates select="myFamily[(SectionName = 'brother')]" mode="nodeCopy"/>
<xsl:apply-templates select="myFamily[(SectionName = 'unle')]" mode="nodeCopy"/>
<xsl:apply-templates select="myFamily[(SectionName = 'aunt')]" mode="nodeCopy"/>
<xsl:apply-templates select="myFamily[(SectionName = 'inLaw')]" mode="nodeCopy"/>
<xsl:apply-templates select="myFamily[(SectionName = 'spouse')]" mode="nodeCopy"/>
<!-- List All Remaining Nodes and Remove ones that have already been ordered above -->
<xsl:apply-templates select="#*|node()[not(parent | sister | brother | spouse | uncle | aunt | inLaw)]"/>
</xsl:copy>
</xsl:template>
<!-- Rename spouse Nodes -->
<xsl:template match="spouse">
<Wife><xsl:apply-templates select="#*|node()" mode="nodeCopy"/></Wife>
</xsl:template>
<!-- Uppercase first letter of elements -->
<xsl:template match="*">
<xsl:element name="{concat(
translate(subsstring(name(.),1,1), $vLower, $vUpper),
substring(name(.), 2, string-length(name(.))-1)
)}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
With regards to the way I want to group it, I feel like this might apply (https://stackoverflow.com/a/16818842/5517100), but I don't understand it. Honestly, I barely understand any of it.
With pre-defined groups as shown in your example, you could do simply:
<xsl:template match="/myFamily">
<MyFamily>
<Parents>
<xsl:apply-templates select="parent"/>
</Parents>
<Siblings>
<xsl:apply-templates select="brother | sister"/>
</Siblings>
<!-- continue for other groups -->
</MyFamily>
</xsl:template>
Or, if you prefer to omit empty groups:
<xsl:template match="/myFamily">
<MyFamily>
<xsl:variable name="parents" select="parent" />
<xsl:if test="$parents">
<Parents>
<xsl:apply-templates select="$parents"/>
</Parents>
</xsl:if>
<!-- continue for other groups -->
</MyFamily>
</xsl:template>

XSLT copy excluding attributes

I've got an XSLT that is translating from XML A to XML B and the 2 are 95% the same except for the fact XML B has fewer attributes. So, for example, I need to translate:
<PaymentDetail PaymentType="CC" ItemNbr="6" CcardNbr="9999999999999999" CcardExp="0523" CcardVend="AA" PayRmrk="" Description="Advance Payment" Total="999.99" />
to
<PaymentDetail PaymentType="CC" ItemNbr="6" CcardNbr="9999999999999999" CcardExp="0523" CcardVend="AA" Total="999.99" />
I've tried copy with an attribute list, but that seems to be long-winded, the same as just doing element = PaymentDetail, attribute name=..... & basically "hard-coding" the whole thing.
I'm using version 1
The standard solution uses template rules:
<!-- Default template: copy nodes unchanged -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Drop selected attributes -->
<xsl:template match="#PayRmrk | #Description"/>
You could exclude the attributes you don't need in your Xpath statement :
<xsl:template match="PaymentDetail">
<xsl:copy>
<xsl:copy-of select="#* except (#PayRmrk, #Description)"/>
</xsl:copy>
</xsl:template>
<!-- Identity template -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
See it working here : http://xsltfiddle.liberty-development.net/6pS26mv

What is the default select of XSLT apply-templates?

The identity template looks like this:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
Does <xsl:apply-templates select="#*|node()" /> select more than <xsl:apply-templates />, or could the identity template have been like this?
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
What exactly is selected when I do the following?
<xsl:apply-templates />
Does <xsl:apply-templates select="#*|node()" /> select more than
<xsl:apply-templates />, or could the identity template have been
like this?
<xsl:apply-templates/>
is equivalent to:
<xsl:apply-templates select="node()"/>
and this is a shorter former of:
<xsl:apply-templates select="child::node()"/>
and this is a equivalent to:
<xsl:apply-templates select="* | text() | comment() | processing-instruction()"/>
As we see from the last instruction, the xsl:apply-templates instruction you are asking about, doesn't select any attributes, therefore it cannot be used as a shorthand for:
<xsl:apply-templates select="#*|node()"/>
The default select for <xsl:apply-templates/> is just "node()", it doesn't include attributes.
The default selection of apply-templates is node(), which is shorthand for child::node(). This XPath expressions is evaluated as follows:
First, all nodes from the "child" axis are taken. This are all the direct children of the current element, i.e. other elements, text, and comments, but not the attributes.
Then this node set is filtered with the node test "node()". In this case, no elements are filtered because that test matches everything.
So with <xsl:apply-templates />, templates for the child elements are applied but not for the attributes. In case of the copy template this would mean that the attributes are not copied.

How to get only the 10 first entry tags using XSLT?

In a feedburner RSS I use
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
to get all the content of a feed with structure like below :
<feed>
<entry><published></published><title></title><content></content>....</entry>
</feed>
My question is, if it is possible to get only the 10 first <entry></entry> instead of all the 25 ? How to do it ?
Note : The entry tag has this form <entry gd:etag="W/"AkcHRH8yfSp7ImA9WhdUFkg.""> I do not know if this matters
smth like
/feed/entry[position()<10]
I mean, you need add this template:
<xsl:template match="entry[position() > 10]"/>
That drop all the entries after 10's, or something equal. The main suggestion is to take a look at position() function.
Your template was recursive. You then changed the behaviour of the outer template, breaking the recursion.
The simplest way to do what you want would be:
<xsl:template match="/feed/entry[position() < 10]">
<xsl:copy-of select="." />
</xsl:template>

uncomment XML content with XSLT

I have a problem with XML and XSLT.
I have one XML file with some comment and I want to uncomment it.
For example:
<my-app>
<name>
</name>
<!-- <class>
<line></line>
</class>-->
</my-app>
I want to uncomment this commented tag.
<!-- the identity template copies everything
(unless more specific templates apply) -->
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<!-- this template matches comments and uncomments them -->
<xsl:template match="comment()">
<xsl:value-of select="." disable-output-escaping="yes" />
</xsl:template>
Be aware that disable-output-escaping="yes" implies that the comment contents should be well-formed.
If one use Saxon, saxon:parse() is cleaner, because it creates a real XML structure.
saxon:parse($xml as xs:string) ==> document-node()
In xsl:stylesheet element, add xmlns:saxon=http://saxon.sf.net/
Example:
<xsl:template match="comment()">
<xsl:variable name="comment" select="saxon:parse(.)" as="document-node()"/>
<xsl:copy-of select="$comment"/>
</xsl:template>