xmlxpathxslt-2.0geonetwork

Xpath - selecting and reading value of all children nodes, regardless of name


I am working on an xsl transformation that is turning an ISO 19139-2 (2009) XML document into some text strings (a GeoNetwork schema plugin's index-fields/index.xsl, which in turn is read by an ElasticSearch instance). Within a particular element, I am trying to:

  1. select all the descendants of that node with a particular exact node name
  2. get all the immediate children of the above node EXCEPT those with a particular name
  3. print the name and value of the above nodes

However, I never seem to get any output from the transformation. From looking at other examples, I feel like I must be missing something very simple, but I can't quite put my finger on it.

I've tried a few different approaches, mostly surrounding how the no-namespace nodes are placed, how specific I am with certain paths, and the use of the period/current node position element.

As far as I can tell, I'm selecting all available MI_AcquisitionInformation, then from there looking for children with the specified names, and then grabbing THEIR children, plus applying the specific name condition, and then reading out the value of those nodes and their names. But instead I get nothing.

<xsl-for-each select="gmi:acquisitionInformation/*">            
  <xsl:for-each select=".//gmi:MI_Operation/*[name() != 'gmi:platform']">
    <operationDetails> <!-- lines like this, without a namespace, end up being the name of the 'bundle' holding the resulting output' -->
      <xsl:value-of select="name()"/>:<xsl:value-of select="."/>
    </operationDetails>
  </xsl:for-each>       

  <xsl:for-each select=".//gmi:MI_Platform/*[name() != 'gmi:instrument']">
    <platformDetails>
      <xsl:value-of select="name()"/>:<xsl:value-of select="."/>
    </platformDetails>
  </xsl:for-each>        

  <xsl:for-each select=".//gmi:MI_Instrument">
    <instrumentDetails>
      <xsl:value-of select="name()"/>:<xsl:value-of select="."/>
    </instrumentDetails>
  </xsl:for-each>        
</xsl-for-each>

A specific detailed example would be...

<gmi:acquisitionInformation>
  <gmi:MI_AcquisitionInformation>
    <gmi:instrument>
      <gmi:MI_Instrument>
        <gmi:identifier>
          (additional content in nodes)
        </gmi:identifier>
        <gmi:type>
          <gco:CharacterString> Text </gco:CharacterString>
        </gmi:type>
      </gmi:MI_Instrument>
    </gmi:instrument>
    <gmi:operation>
      <gmi:MI_Operation>
        <gmi:identifier>
          (additional content in nodes)
        </gmi:identifier>        
        <gmi:citation>
          (additional content in nodes)
        </gmi:citation>
        <gmi:status>
          <gmd:MD_ProgressCode (content in attributes)/>
        </gmi:status>
        <gmi:platform>
          <gmi:MI_Platform>
            <gmi:identifier>
              (additional content in nodes)
            </gmi:identifier>
            <gmi:description>
              <gco:CharacterString> Text </gco:CharacterString>
            </gmi:description>
            <gmi:instrument>
              <gmi:MI_Instrument>
                <gmi:identifier>
                  (additional content in nodes)
                </gmi:identifier>
                <gmi:type>
                  <gco:CharacterString> Text </gco:CharacterString>
                </gmi:type>
              </gmi:MI_Instrument>
            </gmi:instrument>
          </gmi:MI_Platform>
        </gmi:platform>
      </gmi:MI_Operation>
    </gmi:operation>
    <gmi:platform>
      <gmi:MI_Platform>
        <gmi:identifier>
          (additional content in nodes)
        </gmi:identifier>
        <gmi:description>
          <gco:CharacterString> Text </gco:CharacterString>
        </gmi:description>
        <gmi:instrument>
          <gmi:MI_Instrument>
            <gmi:identifier>
              (additional content in nodes)
            </gmi:identifier>
            <gmi:type>
              <gco:CharacterString> Text </gco:CharacterString>
            </gmi:type>
          </gmi:MI_Instrument>
        </gmi:instrument>
      </gmi:MI_Platform>
    </gmi:platform>
  </gmi:MI_AcquisitionInformation>
</gmi:acquisitionInformation>

I would expect something like this for the above sample.

<operationDetails>
  gmi:identifier:(the additional content from its child nodes)
  gmi:citation:(the additional content from its child nodes)
  gmi:status:(Unsure? maybe nothing because the values are in attributes here?)
<!-- note here that there is no platform information -->
</operationDetails>
<platformDetails>
  gmi:identifier:(for the one inside the operation)
  gmi:description:(for the one inside the operation)
</platformDetails>
<platformDetails>
  gmi:identifier:(for the one inside MI_AcquisitionInformation)
  gmi:description:(for the one inside MI_AcquisitionInformation)
<!-- note here that there is no instrument information -->
</platformDetails>
<instrumentDetails>
  gmi:identifier:(for the one inside MI_AcquisitionInformation)
  gmi:type:(for the one inside MI_AcquisitionInformation)
</instrumentDetails>
<instrumentDetails>
  for the one inside platform inside the operation, same sort of info as above...
</instrumentDetails>
<instrumentDetails>
  for the one inside the platform inside MI_AcquisitionInformation, same sort of info as above...
</instrumentDetails>

Mostly, for a record containing an MI_Operation, MI_Platform, and/or MI_Instrument, I expect to see something in ElasticSearch named (something)Details, but that's after another step of processing.

But as mentioned previously, I get nothing, and no error messages - so presumably I am merely selecting on the wrong nodes, rather than actually making an error in the code.


Solution

  • The combination of the typo fix and the better methodology I realized last night seem to be the solution. Apologies that this isn't a fully complete xsl document but the gist of the solution is...

          <xsl:for-each select="gmi:acquisitionInformation/*">        
            <xsl:for-each select=".//gmi:MI_Operation">
              <operationDetails>{
                <xsl:for-each select="./*[name() != 'gmi:platform']">
                  &quot;<xsl:value-of select="name()"/>&quot;:&quot;<xsl:value-of select="."/>&quot;,
                </xsl:for-each>
              }</operationDetails>
            </xsl:for-each>       
    
            <xsl:for-each select=".//gmi:MI_Platform">
              <platformDetails>{
                <xsl:for-each select="./*[name() != 'gmi:instrument']">
                  &quot;<xsl:value-of select="name()"/>&quot;:&quot;<xsl:value-of select="."/>&quot;,
                </xsl:for-each>
              }</platformDetails>
            </xsl:for-each>        
    
           <xsl:for-each select=".//gmi:MI_Instrument">
              <instrumentDetails>{
                <xsl:for-each select="./*">
                  &quot;<xsl:value-of select="name()"/>&quot;:&quot;<xsl:value-of select="."/>&quot;,
                </xsl:for-each>
              }</instrumentDetails>
            </xsl:for-each>      
          </xsl:for-each>
    

    For someone who wants to use this in GeoNetwork, I put this directly under the section for gmd:distributionInfo (starting with line <xsl:for-each select="gmd:distributionInfo/*>) in my schema plugin's index-fields\index.xsl. This picks up the gmi:acquisitionInformation block within gmi:MI_Metadata provided you change the root template matching to accommodate gmi:MI_Metadata as well.