Reputation: 23
I'm trying to replace this XML:
<name type="personal" authority="local">
<namePart>Gertrude</namePart>
<namePart type="termsOfAddress">Aunt</namePart>
<role>
<roleTerm authority="marcrelator" type="text">Correspondent</roleTerm>
<roleTerm authority="marcrelator" type="code">crp</roleTerm>
</role>
</name>
with this XML:
<name type="personal" authority="local">
<namePart>Aunt Gertrude</namePart>
<role>
<roleTerm authority="marcrelator" type="text">Correspondent</roleTerm>
<roleTerm authority="marcrelator" type="code">crp</roleTerm>
</role>
</name>
while still retaining the rest of the document. I've tried two methods: one that works, but seems stupid, and one that doesn't work, and seems just as stupid.
First Method
<xsl:template match="* | processing-instruction() | comment()">
<xsl:copy>
<xsl:copy-of select="@*" copy-namespaces="no"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="//name/namePart[matches(., 'Gertrude')
and following-sibling::namePart[@type='termsOfAddress'
and matches(., 'Aunt')]]">
<namePart>Aunt Gertrude</namePart>
</xsl:template>
<xsl:template match="//name/namePart[@type='termsOfAddress'
and matches(., 'Aunt')
and preceding-sibling::namePart[matches(., 'Gertrude')]]"/>
Seoncd Method
<xsl:template match="* | processing-instruction() | comment()">
<xsl:copy>
<xsl:copy-of select="@*" copy-namespaces="no"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="//name[descendant::namePart[matches(., 'Gertrude')]
and descendant::namePart[@type='termsOfAddress'
and matches(., 'Aunt')]]/namePart">
<namePart>Aunt Gertrude</namePart>
</xsl:template>
So, like I said, the first one works, but having two separate templates to handle the two elements seemed somewhat redundant. So I tried the second method which gave me this:
<name type="personal" authority="local">
<namePart>Aunt Gertrude</namePart>
<namePart>Aunt Gertrude</namePart>
<role>
<roleTerm authority="marcrelator" type="text">Correspondent</roleTerm>
<roleTerm authority="marcrelator" type="code">crp</roleTerm>
</role>
</name>
Which is not what I want.
Is there any way to select both namePart elements and replace it with one?
Upvotes: 2
Views: 468
Reputation: 243579
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:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<namePart>
<xsl:apply-templates select="namePart">
<xsl:sort select="count(@*)"/>
</xsl:apply-templates>
</namePart>
<xsl:apply-templates select="*[not(self::namePart)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="namePart">
<xsl:if test="not(position()=1)"><xsl:text> </xsl:text></xsl:if>
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<name type="personal" authority="local">
<namePart>Gertrude</namePart>
<namePart type="termsOfAddress">Aunt</namePart>
<role>
<roleTerm authority="marcrelator" type="text">Correspondent</roleTerm>
<roleTerm authority="marcrelator" type="code">crp</roleTerm>
</role>
</name>
produces the wanted, correct result:
<name type="personal" authority="local">
<namePart>Gertrude Aunt</namePart>
<role>
<roleTerm authority="marcrelator" type="text">Correspondent</roleTerm>
<roleTerm authority="marcrelator" type="code">crp</roleTerm>
</role>
</name>
Upvotes: 0