DaveF
DaveF

Reputation: 173

XSL key lookup in second file & replace element

I have two xml files:

Source.xml

<osm>
  <count>
    <tag k="total" v="1560"/>
  </count>
  <node>
    <tag k="fhrs:id" v="111111"/>
  </node>
  <node>
    <tag k="fhrs:id" v="222222"/>
    <tag k="addr:postcode" v="XXX XXX"/>
  </node>
  <node>
    <tag k="fhrs:id" v="333333"/>
    <tag k="addr:postcode" v="YYY YYY"/>
  </node>
  <way>
    <tag k="fhrs:id" v="444444"/>
  </way>
  <way>
    <tag k="fhrs:id" v="555555"/>
    <tag  v="ZZZ ZZZ"/>
  </way>
</osm>

Lookup.xml

<FHRSEstablishment>
    <EstablishmentCollection>
        <EstablishmentDetail>
            <FHRSID>111111</FHRSID>
            <PostCode>BA1 111</PostCode>
        </EstablishmentDetail>
        <EstablishmentDetail>
            <FHRSID>333333</FHRSID>
            <PostCode>BA2 222</PostCode>
        </EstablishmentDetail>
        <EstablishmentDetail>
            <FHRSID>555555</FHRSID>
            <PostCode>BA3 333</PostCode>
        </EstablishmentDetail>
    </EstablishmentCollection>
</FHRSEstablishment>

I wish to compare them using the corresponding @v value of the k=fhrs:id attribute in source.xml with the FHRSID node value in lookup.xml to produce an amended version of source.xml.

When a match is found, the value of the Postcode node should be copied across to replace the value of the @v attribute for the tag element with the attribute k="addr:postcode".

This is the XSL fille:

<xsl:key name="FHRSID-key" match="FHRSID" use="node()"/>
<xsl:variable name="lookup-doc" select="doc('lookup.xml')"/>

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

<xsl:template match="*/tag[@v and key('FHRSID-key', @v, $lookup-doc)]">
    <xsl:copy>
        <xsl:attribute name="k">addr:postcode</xsl:attribute>
        <xsl:attribute name="v" select="key('FHRSID-key', @v, $lookup-doc)/../PostCode/node()"/>
  </xsl:copy>
</xsl:template>

This is the current output

<osm>
   <count>
      <tag k="total" v="1560"/>
   </count>
   <node>
      <tag k="addr:postcode" v="BA1 111"/>
   </node>
   <node>
      <tag k="fhrs:id" v="222222"/>
      <tag k="addr:postcode" v="XXX XXX"/>
   </node>
   <node>
      <tag k="addr:postcode" v="BA2 222"/>
      <tag k="addr:postcode" v="YYY YYY"/>
   </node>
   <way>
      <tag k="fhrs:id" v="444444"/>
   </way>
   <way>
      <tag k="addr:postcode" v="BA3 333"/>
      <tag k="addr:postcode" v="ZZZ ZZZ"/>
   </way>
</osm>

This is the desired ouput:

<osm>
    <count>
    <tag k="total" v="1560"/>
    </count>
    <node>
    <tag k="fhrs:id" v="111111"/>
    <tag k="addr:postcode" v="BA1 111"/>
    </node>
    <node>
    <tag k="fhrs:id" v="222222"/>
    <tag k="addr:postcode" v="XXX XXX"/>
    </node>
    <node>
    <tag k="fhrs:id" v="333333"/>
    <tag k="addr:postcode" v="BA2 222"/>
  </node>
  <way>
    <tag k="fhrs:id" v="444444"/>
  </way>
  <way>
    <tag k="fhrs:id" v="555555"/>
    <tag k="addr:postcode" v="BA3 333"/>
  </way>
</osm>

As you can see it's replacing the 'fhrs:id' element with the copied postcode element, instead of the original k="addr:postcode".

Incidental questions:

match="*/tag[@v and key('FHRSID-key', @v, $lookup-doc)]"

Is the first occurrence of @v actually doing anything?

To me, this match appears to be searching all tag elements. If so, how can it be restricted to look just for those with just the k="addr:postcode" attribute.

Any other improvements to be recommended, please feel free.

Upvotes: 0

Views: 104

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167716

Changing the code to match on the parent

<xsl:key name="FHRSID-key" match="EstablishmentDetail" use="FHRSID"/>
<xsl:variable name="lookup-doc" select="doc('lookup.xml')"/>

<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

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

<xsl:template match="*[tag[@k = 'fhrs:id' and key('FHRSID-key', @v, $lookup-doc)]]">
    <xsl:copy>
        <xsl:apply-templates select="@* , node() except tag[@k = 'addr:postcode']"/>
        <tag k="addr:postcode" v="{key('FHRSID-key', tag[@k = 'fhrs:id']/@v, $lookup-doc)/PostCode}"/>
    </xsl:copy>
</xsl:template>

gives

<osm>
   <count>
      <tag k="total" v="1560"/>
   </count>
   <node>
      <tag k="fhrs:id" v="111111"/>
      <tag k="addr:postcode" v="BA1 111"/>
   </node>
   <node>
      <tag k="fhrs:id" v="222222"/>
      <tag k="addr:postcode" v="XXX XXX"/>
   </node>
   <node>
      <tag k="fhrs:id" v="333333"/>
      <tag k="addr:postcode" v="BA2 222"/>
   </node>
   <way>
      <tag k="fhrs:id" v="444444"/>
   </way>
   <way>
      <tag k="fhrs:id" v="555555"/>
      <tag v="ZZZ ZZZ"/>
      <tag k="addr:postcode" v="BA3 333"/>
   </way>
</osm>

where the only problem seems to be the <tag v="ZZZ ZZZ"/> element. I am not sure which are the criteria to copy or not copy existing content, if you only want to keep the tag k="fhrs:id" then change that last template to

<xsl:template match="*[tag[@k = 'fhrs:id' and key('FHRSID-key', @v, $lookup-doc)]]">
    <xsl:copy>
        <xsl:apply-templates select="@* , tag[@k = 'fhrs:id']"/>
        <tag k="addr:postcode" v="{key('FHRSID-key', tag[@k = 'fhrs:id']/@v, $lookup-doc)/PostCode}"/>
    </xsl:copy>
</xsl:template>

Upvotes: 1

Related Questions