user2093335
user2093335

Reputation: 219

XSLT - Bring in external document value that matches on an element

I have XML files where I need to replace the value of an element (idno[@type='code']) with a value from an external document.

I have to match the value based off of an eISBN in that external document.

XML file:

<book>
    <idno type="online">3456789012345</idno>
    <idno type="subject">Environment</idno>
    <idno type="subject">Water</idno>
    <idno type="subject">Policy</idno>
    <idno type="code">135/B</idno>
    <idno type="ID">43396</idno>
</book>

External document (a2r-test.xml):

<root>
    <row>
        <eISBN>1234567890123</eISBN>
        <uri>book1</uri>
        <a2r>89364</a2r>
    </row>
    <row>
        <eISBN>3456789012345</eISBN>
        <uri>book2</uri>
        <a2r>E135</a2r>
    </row>
    <row>
        <eISBN>5678901234567</eISBN>
        <uri>book3</uri>
        <a2r>B10765/B</a2r>
    </row>
</root>

XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tei="http://www.tei-c.org/ns/1.0"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:variable name="a2r" select="document('a2r-test.xml')"/>

    <xsl:template match="tei:idno[@type='code']">
        <xsl:if test="$a2r//row/eISBN = preceding-sibling::tei:idno[@type='online']">
            <xsl:element name="idno">
                <xsl:attribute name="type" select="'code'"/>
                **<xsl:value-of select="?"/>**
            </xsl:element>
        </xsl:if>
    </xsl:template>

<xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

What do I place in the xsl:value-of select to pull in the value of "a2r" (following-sibling::eISBN - that matches the idno[@type='online'])

Final expected result:

<book>
    <idno type="online">3456789012345</idno>
    <idno type="subject">Environment</idno>
    <idno type="subject">Water</idno>
    <idno type="subject">Policy</idno>
    <idno type="code">E135</idno>
    <idno type="ID">43396</idno>
</book>

Thanks in advance.

Upvotes: 0

Views: 1716

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 116992

In general, it's best to use a key to resolve cross-references. Here's an example matching your given inputs:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="a2r-doc" select="document('a2r-test.xml')"/>

<xsl:key name="a2r-key" match="row" use="eISBN" />

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="idno[@type='code']">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:value-of select="key('a2r-key', ../idno[@type='online'], $a2r-doc)/a2r"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Note:

You didn't show us your real input, which - judging from your XSLT - is in a namespace. In such case, you cannot use xsl:copy (through the identity transform template) for some elements and xsl:element (without a namespace) for others, or you will end up with the result being a mix of elements in different namespaces.

Upvotes: 3

Related Questions