Jing
Jing

Reputation:

xpath question on selecting node set

Here is a simplified version of my source xml document:

<eml> 
<additionalMetadata>
    <describes>sbclter.380</describes>
    <access authSystem="knb" order="denyFirst">
      <allow>
        <principal>public</principal>
        <permission>all</permission>
      </allow>
    </access>
  </additionalMetadata>

  <additionalMetadata>
    <describes>sbclter.415</describes>
    <describes>sbclter.380</describes>
      <access authSystem="knb" order="allowFirst">
        <allow>
          <principal>public</principal>
          <permission>all</permission>
        </allow>
      </access>
  </additionalMetadata>

  <additionalMetadata>
        <describes>sbclter.415</describes>
        <access authSystem="knb" order="allowFirst">
            <allow>
              <principal>public</principal>
              <permission>all</permission>
            </allow>
          </access>
    </additionalMetadata>
 <eml>

I want to select a node set of element "describes" - its value shows in more than one addtionalMetadata block and its "access" sibling has different order: "denyFirst" and "allowFirst".

In above example, the "describes" with value sbclter.380 is the case. Since it shows in two additionalMetadata block, the first one has "denyFirst" in access sibling, but the second one has "allowFirst" in access sibling. sbclter.415 would NOT be the case. Since it shows up in two additionalMetadata block, but both access siblings has the same value "allowFirst" at the attribute "order".

After transformation, i want get:

<eml> 
    <additionalMetadata>
        <describes>sbclter.380</describes>
        <access authSystem="knb" order="denyFirst">
          <allow>
            <principal>public</principal>
            <permission>all</permission>
          </allow>
        </access>
      </additionalMetadata>

      <additionalMetadata>
        <describes>sbclter.380</describes>
          <access authSystem="knb" order="allowFirst">
            <allow>
              <principal>public</principal>
              <permission>all</permission>
            </allow>
          </access>
      </additionalMetadata>

      <additionalMetadata>
            <access authSystem="knb" order="allowFirst">
                <allow>
                  <principal>public</principal>
                  <permission>all</permission>
                </allow>
              </access>
        </additionalMetadata>
     <eml>

Here is my node:

   <xsl:for-each select="/*/*">
        <xsl:choose>
           <xsl:when test="name()='additionalMetadata'">
            <xsl:call-template name="handle-describe-access-in-additional-metadata">
                <xsl:with-param name="describes-list" select="./describes[//additionalMetadata[describes = . and access[@order='allowFirst']] and //additionalMetadata[describes = . and access[@order='denyFirst']]]"
          ></xsl:with-param>
              ></xsl:with-param>
            </xsl:call-template>
          </xsl:when>
        </xsl:choose>
      </xsl:for-each>

However,it doesn't work. The xpath in "xsl:with-param" picked up nothing - no sbclter.415 either sbclter.380. Do you have any suggestion? Thank you so much!

Note: this is simplified version of code.

Upvotes: 1

Views: 1015

Answers (3)

Welbog
Welbog

Reputation: 60378

Elaborating on what I said in my comment to your answer, here's an example that demonstrates what I mean:

Using this XML file as a source,

<Header>
  <thing>
    <child>5</child>
  </thing>
  <thing>
    <child>6</child>
  </thing>
  <thing>
    <child>7</child>
  </thing>
  <thang>
    <children>5</children>
  </thang>
</Header>

With this XSLT file:

<xsl:template match="/Header">
    <xsl:value-of select="./thing[//thang[children = ./child]]"/>
</xsl:template>

If the . refered to thing in this example, the output would be 5, but since . refers to thang, there is no output. If there were a variable that contained thing, then it would work:

  <xsl:template match="/Header">
    <xsl:variable name="thing" select="./thing"/>
    <xsl:value-of select="./thing[//thang[children = $thing/child]]"/>
  </xsl:template>

You're in the same situation. Your . is referencing something other than what you think it is.

Upvotes: 0

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243459

Here is one transformation that produces the wanted result:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes"/>
<!--                                                --> 
 <xsl:key name="kDescrByVal" match="describes"
  use="."/>
<!--                                                --> 
    <xsl:template match="node()|@*" name="identity">
      <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>
<!--                                                --> 
    <xsl:template match="describes">
      <xsl:if test=
       "count(key('kDescrByVal', .)) > 1
       and
        following-sibling::access/@order
       !=
        key('kDescrByVal', .)/following-sibling::access/@order
       ">
       <xsl:call-template name="identity"/>
      </xsl:if>
    </xsl:template>
</xsl:stylesheet>

When applied to the provided XML document (corrected to be well-formed):

<eml>
    <additionalMetadata>
        <describes>sbclter.380</describes>
        <access authSystem="knb" order="denyFirst">
            <allow>
                <principal>public</principal>
                <permission>all</permission>
            </allow>
        </access>
    </additionalMetadata>
    <additionalMetadata>
        <describes>sbclter.415</describes>
        <describes>sbclter.380</describes>
        <access authSystem="knb" order="allowFirst">
            <allow>
                <principal>public</principal>
                <permission>all</permission>
            </allow>
        </access>
    </additionalMetadata>
    <additionalMetadata>
        <describes>sbclter.415</describes>
        <access authSystem="knb" order="allowFirst">
            <allow>
                <principal>public</principal>
                <permission>all</permission>
            </allow>
        </access>
    </additionalMetadata>
</eml>

the wanted result is produced:

<eml>
    <additionalMetadata>
        <describes>sbclter.380</describes>
        <access authSystem="knb" order="denyFirst">
            <allow>
                <principal>public</principal>
                <permission>all</permission>
            </allow>
        </access>
    </additionalMetadata>
    <additionalMetadata>

        <describes>sbclter.380</describes>
        <access authSystem="knb" order="allowFirst">
            <allow>
                <principal>public</principal>
                <permission>all</permission>
            </allow>
        </access>
    </additionalMetadata>
    <additionalMetadata>

        <access authSystem="knb" order="allowFirst">
            <allow>
                <principal>public</principal>
                <permission>all</permission>
            </allow>
        </access>
    </additionalMetadata>
</eml>

Do note the use of:

  1. Keys.

  2. The Xpath "!=" operator.

Upvotes: 1

Welbog
Welbog

Reputation: 60378

I would try making a variable of the current describes node and using it instead of referencing . in your XPath subexpressions.

Because, for example, in the statement [//additionalMetadata[describes = ., the . is referenceing the additionalMetadata node, not the describes node.

Upvotes: 0

Related Questions