0

I am attempting to loop through a set of nodes to build a menu of distinct, sorted values. All the possible values are known, but which and how many are included in the XML is not known at run time. The known list changes from time to time, so including the full list would require maintenance control I would rather avoid.

The error text is Required item type of the context item for the parent axis is node(); supplied value has item type xs:anyAtomicType.

I am using XSLT 2.0 from Xerces embedded in Solr 7.6.

I think I understand why, because I am contextually at a text value and not at a node, but I do not know a way around it.

Here is an example XML...

<?xml version="1.0" encoding="UTF-8"?>
<response>

<result name="response" numFound="25" start="0">
  <doc>
    <str name="id">REF-50102</str>
    <str name="type-name">Reference</str>
    <str name="type-abbrv">REF</str>
  </doc>
  <doc>
    <str name="id">REF-50222</str>
    <str name="type-name">Reference</str>
    <str name="type-abbrv">REF</str>
  </doc>
  <doc>
    <str name="id">REF-50245</str>
    <str name="type-name">Reference</str>
    <str name="type-abbrv">REF</str>
  </doc>
  <doc>
    <str name="id">REF-50029</str>
    <str name="type-name">Reference</str>
    <str name="type-abbrv">REF</str>
  </doc>
  <doc>
    <str name="id">SUB-4</str>
    <str name="type-name">Subsystem</str>
    <str name="type-abbrv">SUB</str>
    <str name="system-name">AC-9</str>
  </doc>
  <doc>
    <str name="id">STK-80</str>
    <str name="type-name">Stack</str>
    <str name="type-abbrv">STK</str>
    <str name="system-name">AC-9</str>
  </doc>
  <doc>
    <str name="id">STK-61</str>
    <str name="type-name">Stack</str>
    <str name="type-abbrv">STK</str>
    <str name="system-name">AC-9</str>
  </doc>
  <doc>
    <str name="id">FLM-50025</str>
    <str name="type-name">Flavor</str>
    <str name="type-abbrv">FLM</str>
    <str name="system-name">AC-9</str>
  </doc>
  <doc>
    <str name="id">FLM-50108</str>
    <str name="type-name">Flavor</str>
    <str name="type-abbrv">FLM</str>
    <str name="system-name">AC-9</str>
  </doc>
  <doc>
    <str name="id">FLM-50109</str>
    <str name="type-name">Flavor</str>
    <str name="type-abbrv">FLM</str>
    <str name="system-name">AC-9</str>
  </doc>
  <doc>
    <str name="id">PFP-50101</str>
    <str name="type-name">Prefabricated</str>
    <str name="type-abbrv">PFP</str>
    <str name="system-name">AC-9</str>
  </doc>
  <doc>
    <str name="id">PFP-50103</str>
    <str name="type-name">Prefabricated</str>
    <str name="type-abbrv">PFP</str>
    <str name="system-name">AC-9</str>
  </doc>
  <doc>
    <str name="id">DSG-50163</str>
    <str name="type-name">Design Drawing</str>
    <str name="type-abbrv">DSG</str>
    <str name="system-name">AB-4</str>
  </doc>
  <doc>
    <str name="id">DSG-50164</str>
    <str name="type-name">Design Drawing</str>
    <str name="type-abbrv">DSG</str>
    <str name="system-name">AC-8</str>
  </doc>
  <doc>
    <str name="id">DSG-50162</str>
    <str name="type-name">Design Drawing</str>
    <str name="type-abbrv">DSG</str>
    <str name="system-name">AB-4</str>
  </doc>
  <doc>
    <str name="id">PXS-81</str>
    <str name="type-name">Production Assembly</str>
    <str name="type-abbrv">PXS</str>
    <str name="system-name">AC-9</str>
  </doc>
  <doc>
    <str name="id">PXS-83</str>
    <str name="type-name">Production Assembly</str>
    <str name="type-abbrv">PXS</str>
    <str name="system-name">AC-9</str>
  </doc>
  <doc>
    <str name="id">PXS-82</str>
    <str name="type-name">Production Assembly</str>
    <str name="type-abbrv">PXS</str>
    <str name="system-name">AC-9</str>
  </doc>
  <doc>
    <str name="id">DWG-42</str>
    <str name="type-name">Drawing</str>
    <str name="type-abbrv">DWG</str>
    <str name="system-name">AC-9</str>
  </doc>
</result>
</response>

And the XSL transform with the errors causing lines are commented out...

<?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"
    exclude-result-prefixes="xs"
    version="2.0">
    <!-- =========================================================================================== -->
    <xsl:output method="html" indent="yes"/>
    <!-- =========================================================================================== -->
    <xsl:template match="/response">
        <html>
            <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
            </head>
            <body>
                <xsl:comment>Working if I know the list, but I do not.</xsl:comment>
                <xsl:apply-templates select="result" mode="countKnownList"/>
                <hr/>
                <xsl:comment>Finds list dynamically, but can not get name or count.</xsl:comment>
                <xsl:apply-templates select="result" mode="countUnknownList"/>
            </body>
        </html>
    </xsl:template>
    <!-- =========================================================================================== -->
    <xsl:template name="listElementUnknownList">
        <xsl:param name="abbrv"/>
        <xsl:param name="name"/>
<!-- What I have tried unsuccessfully.
        <xsl:variable name="quantity"><xsl:value-of select="count(//doc/str[@name='type-abbrv']/text()='$abbrv')"/></xsl:variable>
 -->
        <xsl:variable name="quantity">#</xsl:variable>
        <li><label>
            <xsl:element name="input">
                <xsl:attribute name="id"><xsl:value-of select="$abbrv"/></xsl:attribute>
                <xsl:attribute name="type">checkbox</xsl:attribute>
                <xsl:attribute name="checked">true</xsl:attribute>
                <xsl:attribute name="onclick">toggleRows('<xsl:value-of select="$abbrv"/>');</xsl:attribute>
            </xsl:element><xsl:value-of select="$name"/>(<xsl:value-of select="$quantity" />)</label></li>
    </xsl:template>
    <!-- =========================================================================================== -->
     <xsl:template match="result" mode="countUnknownList">
        <dl>
            <dt>Type (<xsl:value-of select="count(doc)" />)</dt>
            <dd>
                <ul>
                    <xsl:for-each select="distinct-values(doc/str[@name='type-abbrv'])">
                        <xsl:sort select="."/>
                        <xsl:variable name="typeAbbrv"><xsl:value-of select="."/></xsl:variable>
<!--  What I have tried unsuccessfully.
                        <xsl:variable name="typeName"><xsl:value-of select="../str[@name='type-name']"/></xsl:variable>
 -->
                        <xsl:variable name="typeName"><xsl:value-of select="$typeAbbrv"/>-name</xsl:variable>
                        <xsl:call-template name="listElementUnknownList">
                            <xsl:with-param name="abbrv"><xsl:value-of select="$typeAbbrv"/></xsl:with-param>
                            <xsl:with-param name="name"><xsl:value-of select="$typeName"/></xsl:with-param>
                        </xsl:call-template>
                    </xsl:for-each>
                 </ul>
            </dd>
            <dt>System (<xsl:value-of select="count(doc)" />)</dt>
            <dd>
                <ul>
                    <xsl:for-each select="distinct-values(doc/str[@name='system-name']/text())">
                        <xsl:sort select="."/>
                        <xsl:variable name="sysName"><xsl:value-of select="."/></xsl:variable>
                        <xsl:call-template name="listElementUnknownList">
                            <xsl:with-param name="abbrv"><xsl:value-of select="$sysName"/></xsl:with-param>
                            <xsl:with-param name="name"><xsl:value-of select="$sysName"/></xsl:with-param>
                        </xsl:call-template>
                    </xsl:for-each>
                    <xsl:call-template name="listElementUnknownList">
                        <xsl:with-param name="abbrv">None</xsl:with-param>
                        <xsl:with-param name="name">None</xsl:with-param>
                    </xsl:call-template>
                </ul>
            </dd>
        </dl>
    </xsl:template>
    <!-- =========================================================================================== -->
    <xsl:template name="listElementKnownList">
        <xsl:param name="abbrv"/>
        <xsl:param name="name"/>
        <xsl:param name="quantity"/>
        <li><label>
            <xsl:element name="input">
                <xsl:attribute name="id"><xsl:value-of select="$abbrv"/></xsl:attribute>
                <xsl:attribute name="type">checkbox</xsl:attribute>
                <xsl:attribute name="checked">true</xsl:attribute>
                <xsl:attribute name="onclick">toggleRows('<xsl:value-of select="$abbrv"/>');</xsl:attribute>
            </xsl:element><xsl:value-of select="$name"/>(<xsl:value-of select="$quantity" />)</label></li>
    </xsl:template>
    <!-- =========================================================================================== -->
     <xsl:template match="result" mode="countKnownList">
        <dl>
            <dt>Type (<xsl:value-of select="count(doc)" />)</dt>
            <dd>
                <ul>
                    <xsl:call-template name="listElementKnownList">
                        <xsl:with-param name="abbrv">DSG</xsl:with-param>
                        <xsl:with-param name="name">Design Drawing</xsl:with-param>
                        <xsl:with-param name="quantity"><xsl:value-of select="count(doc[str[@name='type-abbrv']/text()='DSG'])"/></xsl:with-param>
                    </xsl:call-template>
                    <xsl:call-template name="listElementKnownList">
                        <xsl:with-param name="abbrv">FLM</xsl:with-param>
                        <xsl:with-param name="name">Flavor</xsl:with-param>
                        <xsl:with-param name="quantity"><xsl:value-of select="count(doc[str[@name='type-abbrv']/text()='FLM'])"/></xsl:with-param>
                    </xsl:call-template>
                    <xsl:call-template name="listElementKnownList">
                        <xsl:with-param name="abbrv">STK</xsl:with-param>
                        <xsl:with-param name="name">Stack</xsl:with-param>
                        <xsl:with-param name="quantity"><xsl:value-of select="count(doc[str[@name='type-abbrv']/text()='STK'])"/></xsl:with-param>
                    </xsl:call-template>
                    <!-- etc -->
                </ul>
            </dd>
        </dl>
    </xsl:template>
    <!-- =========================================================================================== -->
</xsl:stylesheet>

And finally, the result with the placeholders for unavailable data...

<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   </head>
   <body>
      <!--Working if I know the list, but I do not.-->
      <dl>
         <dt>Type (19)</dt>
         <dd>
            <ul>
               <li><label><input id="DSG" type="checkbox" checked="true" onclick="toggleRows('DSG');">Design Drawing(3)</label></li>
               <li><label><input id="FLM" type="checkbox" checked="true" onclick="toggleRows('FLM');">Flavor(3)</label></li>
               <li><label><input id="STK" type="checkbox" checked="true" onclick="toggleRows('STK');">Stack(2)</label></li>
            </ul>
         </dd>
      </dl>
      <hr>
      <!--Finds list dynamically, but can not get name or count.-->
      <dl>
         <dt>Type (19)</dt>
         <dd>
            <ul>
               <li><label><input id="DSG" type="checkbox" checked="true" onclick="toggleRows('DSG');">DSG-name(#)</label></li>
               <li><label><input id="DWG" type="checkbox" checked="true" onclick="toggleRows('DWG');">DWG-name(#)</label></li>
               <li><label><input id="FLM" type="checkbox" checked="true" onclick="toggleRows('FLM');">FLM-name(#)</label></li>
               <li><label><input id="PFP" type="checkbox" checked="true" onclick="toggleRows('PFP');">PFP-name(#)</label></li>
               <li><label><input id="PXS" type="checkbox" checked="true" onclick="toggleRows('PXS');">PXS-name(#)</label></li>
               <li><label><input id="REF" type="checkbox" checked="true" onclick="toggleRows('REF');">REF-name(#)</label></li>
               <li><label><input id="STK" type="checkbox" checked="true" onclick="toggleRows('STK');">STK-name(#)</label></li>
               <li><label><input id="SUB" type="checkbox" checked="true" onclick="toggleRows('SUB');">SUB-name(#)</label></li>
            </ul>
         </dd>
         <dt>System (19)</dt>
         <dd>
            <ul>
               <li><label><input id="AB-4" type="checkbox" checked="true" onclick="toggleRows('AB-4');">AB-4(#)</label></li>
               <li><label><input id="AC-8" type="checkbox" checked="true" onclick="toggleRows('AC-8');">AC-8(#)</label></li>
               <li><label><input id="AC-9" type="checkbox" checked="true" onclick="toggleRows('AC-9');">AC-9(#)</label></li>
               <li><label><input id="None" type="checkbox" checked="true" onclick="toggleRows('None');">None(#)</label></li>
            </ul>
         </dd>
      </dl>
   </body>
</html
4

2 回答 2

1

该变量解决了您的问题,但如果您只使用xsl:for-each-group例如,您将不会拥有它

   <xsl:for-each-group select="doc" group-by="str[@name='type-abbrv']">
                    <xsl:sort select="current-grouping-key()"/>
                    <xsl:variable name="typeAbbrv" select="current-grouping-key()"/>

                    <xsl:variable name="typeName" select="str[@name='type-name']"/>

                    <xsl:variable name="typeName"><xsl:value-of select="$typeAbbrv"/>-name</xsl:variable>
                    <xsl:call-template name="listElementUnknownList">
                        <xsl:with-param name="abbrv"><xsl:value-of select="$typeAbbrv"/></xsl:with-param>
                        <xsl:with-param name="name"><xsl:value-of select="$typeName"/></xsl:with-param>
                    </xsl:call-template>
   </xsl:for-each-group>

当然可以缩短为

   <xsl:for-each-group select="doc" group-by="str[@name='type-abbrv']">
                    <xsl:sort select="current-grouping-key()"/>
                    <xsl:call-template name="listElementUnknownList">
                        <xsl:with-param name="abbrv" select="current-grouping-key()"/>
                        <xsl:with-param name="name" select="concat(str[@name='type-name'], '-name')"/>
                    </xsl:call-template>
   </xsl:for-each-group>

所以我认为分组是你真正想要的,我试图将其中的一部分实现为

 <xsl:template match="result" mode="countUnknownList">
    <dl>
        <dt>Type (<xsl:value-of select="count(doc)" />)</dt>
        <dd>
            <ul>
               <xsl:for-each-group select="doc" group-by="str[@name='type-abbrv']">
                    <xsl:sort select="current-grouping-key()"/>
                    <li>
                       <label>
                           <input id="{current-grouping-key()}"
                                  type="checkbox"
                                  checked="checked"
                                  onclick="toggleRows('{current-grouping-key()}');"/>
                            <xsl:value-of select="concat(str[@name='type-name'], '(', count(current-group()), ')')"/>

                       </label> 
                    </li>
               </xsl:for-each-group>
             </ul>
        </dd>
        <dt>System (<xsl:value-of select="count(doc)" />)</dt>
        <dd>
            <ul>
               <xsl:for-each-group select="doc" group-by="str[@name='system-name']">
                    <xsl:sort select="current-grouping-key()"/>
                    <li>
                       <label>
                           <input id="{current-grouping-key()}"
                                  type="checkbox"
                                  checked="checked"
                                  onclick="toggleRows('{current-grouping-key()}');"/>
                            <xsl:value-of select="concat(current-grouping-key(), '(', count(current-group()), ')')"/>

                       </label> 
                    </li>
               </xsl:for-each-group>                    
            </ul>
        </dd>
    </dl>
</xsl:template>

https://xsltfiddle.liberty-development.net/gWvjQf4,您的样本的结果有一个列表

        <ul>
           <li><label><input id="DSG" type="checkbox" checked onclick="toggleRows('DSG');">Design Drawing(3)</label></li>
           <li><label><input id="DWG" type="checkbox" checked onclick="toggleRows('DWG');">Drawing(1)</label></li>
           <li><label><input id="FLM" type="checkbox" checked onclick="toggleRows('FLM');">Flavor(3)</label></li>
           <li><label><input id="PFP" type="checkbox" checked onclick="toggleRows('PFP');">Prefabricated(2)</label></li>
           <li><label><input id="PXS" type="checkbox" checked onclick="toggleRows('PXS');">Production Assembly(3)</label></li>
           <li><label><input id="REF" type="checkbox" checked onclick="toggleRows('REF');">Reference(4)</label></li>
           <li><label><input id="STK" type="checkbox" checked onclick="toggleRows('STK');">Stack(2)</label></li>
           <li><label><input id="SUB" type="checkbox" checked onclick="toggleRows('SUB');">Subsystem(1)</label></li>
        </ul>

第二个为

        <ul>
           <li><label><input id="AB-4" type="checkbox" checked onclick="toggleRows('AB-4');">AB-4(2)</label></li>
           <li><label><input id="AC-8" type="checkbox" checked onclick="toggleRows('AC-8');">AC-8(1)</label></li>
           <li><label><input id="AC-9" type="checkbox" checked onclick="toggleRows('AC-9');">AC-9(12)</label></li>
        </ul>

希望我已经掌握了原始代码的意图。

于 2019-02-24T19:17:15.260 回答
1

避免这种情况的一种方法是定义一个包含整个文档的顶级变量,就像在这个答案中一样:

...
<xsl:output method="html" indent="yes"/>
<xsl:variable name="mainDoc" select="/" />
...

然后在注释掉的变量中引用它(我还更改了 中的谓词count以计算所需的节点):

首先

<xsl:variable name="quantity" select="count($mainDoc/response/result/doc/str[@name='type-abbrv' and text()=$abbrv])"/>

第二个

<xsl:variable name="typeName" select="($mainDoc//doc[str[@name='type-abbrv']/text()=$typeAbbrv]/str[@name='type-name'])[1]" />
于 2019-02-21T17:51:14.510 回答