用于创建 table 的 XSLT 方案,其 header 和行是独立且分层的

XSLT scheme for create a table whose header and rows are separate and hierarchical

我在将这样的文件转换为“平面”文件时遇到了问题table 这些文件在交换期间上传。它们有很多,但它们只有一个模板。首先是 headers 的描述,然后是值。

<?xml version="1.0" encoding="UTF-8"?>
<extdata user="test">
   <scheme name="CRMOrder" request="get" success="true">
      <data>
         <s>
            <d name="CRMOrder">
               <f name="ActionDate" type="Date" />
               <f name="CRMClientId" type="String" />
               <f name="CreateId" type="Date" />
               <f name="StatusId" type="String" />
               <f name="Summa" type="Decimal" />
               <f name="WareHouseId" type="String" />
               <d name="CRMOrderLine">
                  <f name="Price" type="Decimal" />
                  <f name="LineNumber" type="Integer" />
                  <f name="Quantity" type="Decimal" />
                  <f name="Discount" type="Integer" />
               </d>
               <d name="CRMOrderOption">
                  <f name="OptionTypeId" type="String" />
                  <f name="Value" type="String" />
                  <f name="OptionTypeName" type="String" />
               </d>
            </d>
         </s>
         <o>
            <d name="CRMOrder">
               <r>
                  <f>2022-01-11T00:00:00</f>
                  <f>69244</f>
                  <f>2142256774</f>
                  <f>Accepted</f>
                  <f>2318.0600</f>
                  <f>62</f>
                  <d name="CRMOrderLine">
                     <r>
                        <f>64.7800</f>
                        <f>1</f>
                        <f>18.0000</f>
                        <f>62</f>
                     </r>
                     <d name="CRMOrderOption">
                        <r>
                           <f>2022-01-10T00:00:00</f>
                           <f>Comment</f>
                           <f>1</f>
                        </r>
                     </d>
                  </d>
               </r>
            </d>
         </o>
      </data>
   </scheme>
</extdata>

XSLT

   <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
       <xsl:output method="xml" indent="yes" />
       <xsl:template match="/extdata/scheme/data">
          <ValueTable xmlns="http://v8.1c.ru/8.1/data/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<-- get the total number of columns -->
             <xsl:variable name="recordColumn" select="count(s/d/f/@name) + count(s/d/d/f/@name)" />
<-- I didn’t understand how to create headers in one cycle, so there are two cycles with the same content -->
             <xsl:for-each select="s/d/f">
                <column>
                   <Name xsi:type="xs:string">
                      <xsl:value-of select="@name" />
                   </Name>
                   <ValueType>
                      <xsl:if test="@type= 'String'">
                         <Type>xs:string</Type>
                         <StringQualifiers>
                            <Length>150</Length>
                            <AllowedLength>Variable</AllowedLength>
                         </StringQualifiers>
                      </xsl:if>
                      <xsl:if test="@type= 'Date'">
                         <Type>xs:dateTime</Type>
                         <DateQualifiers>
                            <DateFractions>DateTime</DateFractions>
                         </DateQualifiers>
                      </xsl:if>
                      <xsl:if test="@type= 'Decimal' or @type= 'Currency'">
                         <Type>xs:decimal</Type>
                         <NumberQualifiers>
                            <Digits>20</Digits>
                            <FractionDigits>4</FractionDigits>
                            <AllowedSign>Any</AllowedSign>
                         </NumberQualifiers>
                      </xsl:if>
                      <xsl:if test="@type= 'Integer'">
                         <Type>xs:decimal</Type>
                         <NumberQualifiers>
                            <Digits>20</Digits>
                            <FractionDigits>0</FractionDigits>
                            <AllowedSign>Any</AllowedSign>
                         </NumberQualifiers>
                      </xsl:if>
                   </ValueType>
                </column>
             </xsl:for-each>
             <xsl:for-each select="s/d/d/f">
                <column>
                   <Name xsi:type="xs:string">
                      <xsl:value-of select="@name" />
                   </Name>
                   <ValueType>
                      <xsl:if test="@type= 'String'">
                         <Type>xs:string</Type>
                         <StringQualifiers>
                            <Length>150</Length>
                            <AllowedLength>Variable</AllowedLength>
                         </StringQualifiers>
                      </xsl:if>
                      <xsl:if test="@type= 'Date'">
                         <Type>xs:dateTime</Type>
                         <DateQualifiers>
                            <DateFractions>DateTime</DateFractions>
                         </DateQualifiers>
                      </xsl:if>
                      <xsl:if test="@type= 'Decimal' or @type= 'Currency'">
                         <Type>xs:decimal</Type>
                         <NumberQualifiers>
                            <Digits>20</Digits>
                            <FractionDigits>4</FractionDigits>
                            <AllowedSign>Any</AllowedSign>
                         </NumberQualifiers>
                      </xsl:if>
                      <xsl:if test="@type= 'Integer'">
                         <Type>xs:decimal</Type>
                         <NumberQualifiers>
                            <Digits>20</Digits>
                            <FractionDigits>0</FractionDigits>
                            <AllowedSign>Any</AllowedSign>
                         </NumberQualifiers>
                      </xsl:if>
                   </ValueType>
                </column>
             </xsl:for-each>
             <xsl:for-each select="o/d/r/f">
<-- Problem is here -->
                <xsl:variable name="counter" select="position()" />
                <xsl:if test="$counter = 1">
                   <xsl:text disable-output-escaping="yes">&lt;row&gt;</xsl:text>
                </xsl:if>
                <xsl:if test="$counter mod $recordColumn = 0">
                   <xsl:text disable-output-escaping="yes">&lt;/row&gt;</xsl:text>
                   <xsl:if test="$counter != last() ">
                      <xsl:text disable-output-escaping="yes">&lt;row&gt;</xsl:text>
                   </xsl:if>
                </xsl:if>
                <xsl:if test="$counter mod $recordColumn != 0">
                   <Value>
                      <xsl:value-of select="." />
                   </Value>
                </xsl:if>
             </xsl:for-each>
          </ValueTable>
       </xsl:template>
    </xsl:stylesheet>

如何从此文件中获取 table。关于字符串和值的行的开头和结尾可能看起来不是很漂亮,但它确实有效。现在有 12 列,因此,我的 table 行仅包含 6 个第一个值。

预期结果

  <ValueTable xmlns="http://v8.1c.ru/8.1/data/core"
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <column>
      <Name xsi:type="xs:string">ActionDate</Name>
      <ValueType>
         <Type>xs:dateTime</Type>
         <DateQualifiers>
            <DateFractions>DateTime</DateFractions>
         </DateQualifiers>
      </ValueType>
   </column>
   <column>
      <Name xsi:type="xs:string">CRMClientId</Name>
      <ValueType>
         <Type>xs:string</Type>
         <StringQualifiers>
            <Length>150</Length>
            <AllowedLength>Variable</AllowedLength>
         </StringQualifiers>
      </ValueType>
   </column>
    <column>
      <Name xsi:type="xs:string">CreateId</Name>
      <ValueType>
         <Type>xs:dateTime</Type>
         <DateQualifiers>
            <DateFractions>DateTime</DateFractions>
         </DateQualifiers>
      </ValueType>
   </column>
   <column>
      <Name xsi:type="xs:string">StatusId</Name>
      <ValueType>
         <Type>xs:string</Type>
         <StringQualifiers>
            <Length>150</Length>
            <AllowedLength>Variable</AllowedLength>
         </StringQualifiers>
      </ValueType>
   </column>
   <column>
      <Name xsi:type="xs:string">Summa</Name>
      <ValueType>
         <Type>xs:decimal</Type>
         <NumberQualifiers>
            <Digits>20</Digits>
            <FractionDigits>4</FractionDigits>
            <AllowedSign>Any</AllowedSign>
         </NumberQualifiers>
      </ValueType>
   </column>
   <column>
      <Name xsi:type="xs:string">WareHouseId</Name>
      <ValueType>
         <Type>xs:string</Type>
         <StringQualifiers>
            <Length>150</Length>
            <AllowedLength>Variable</AllowedLength>
         </StringQualifiers>
      </ValueType>
   </column>
    <column>
      <Name xsi:type="xs:string">Price</Name>
      <ValueType>
         <Type>xs:decimal</Type>
         <NumberQualifiers>
            <Digits>20</Digits>
            <FractionDigits>4</FractionDigits>
            <AllowedSign>Any</AllowedSign>
         </NumberQualifiers>
      </ValueType>
   </column>
   <column>
      <Name xsi:type="xs:string">LineNumber</Name>
      <ValueType>
         <Type>xs:decimal</Type>
         <NumberQualifiers>
            <Digits>20</Digits>
            <FractionDigits>0</FractionDigits>
            <AllowedSign>Any</AllowedSign>
         </NumberQualifiers>
      </ValueType>
   </column>
   <column>
      <Name xsi:type="xs:string">Quantity</Name>
      <ValueType>
         <Type>xs:decimal</Type>
         <NumberQualifiers>
            <Digits>20</Digits>
            <FractionDigits>4</FractionDigits>
            <AllowedSign>Any</AllowedSign>
         </NumberQualifiers>
      </ValueType>
   </column>
    <column>
      <Name xsi:type="xs:string">Discount</Name>
      <ValueType>
         <Type>xs:decimal</Type>
         <NumberQualifiers>
            <Digits>20</Digits>
            <FractionDigits>0</FractionDigits>
            <AllowedSign>Any</AllowedSign>
         </NumberQualifiers>
      </ValueType>
   </column>
       <Name xsi:type="xs:string">OptionTypeId</Name>
      <ValueType>
         <Type>xs:string</Type>
         <StringQualifiers>
            <Length>150</Length>
            <AllowedLength>Variable</AllowedLength>
         </StringQualifiers>
      </ValueType>
   </column>
   <column>
      <Name xsi:type="xs:string">Value</Name>
      <ValueType>
         <Type>xs:string</Type>
         <StringQualifiers>
            <Length>150</Length>
            <AllowedLength>Variable</AllowedLength>
         </StringQualifiers>
      </ValueType>
   </column>
   <column>
      <Name xsi:type="xs:string">OptionTypeName</Name>
      <ValueType>
         <Type>xs:string</Type>
         <StringQualifiers>
            <Length>150</Length>
            <AllowedLength>Variable</AllowedLength>
         </StringQualifiers>
      </ValueType>
   </column>
<row>
 <Value>2022-01-11T00:00:00</Value>
 <Value>69244</Value> 
 <Value>2142256774</Value>
 <Value>Accepted</Value>
 <Value>2318.0600</Value>
 <Value>62</Value>
 <Value>64.7800</Value>
 <Value>1</Value>
 <Value>18.0000</Value>
 <Value>62</Value>
 <Value>2022-01-10T00:00:00</Value>
 <Value>Comment</Value>
 <Value>1</Value>
 </row>
  </ValueTable>

很遗憾,您提供的 XML 无效。而且您没有提供输出的示例。

但是,这只是对您的主要问题的快速、希望有用的回答。 如何避免重复整个 header 定义 或其他样式表部分。

您可以在 XPath 中使用 OR 多个路径

<xsl:for-each select="s/d/f | s/d/d/f">

给出的例子有些含糊。假设 s 是 header 行,并且 o 是一个数据行,其中 header 行中的每一列都恰好包含一个值,并且顺序相同(IOW ,输入已经是一个“平面”table),显示的结果可以使用:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://v8.1c.ru/8.1/data/core"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/extdata">
    <ValueTable>
        <!-- header -->
        <xsl:for-each select="scheme/data/s//f">
            <column>
                <Name xsi:type="xs:string">
                    <xsl:value-of select="@name"/>
                </Name>
                <ValueType>
                    <xsl:choose>
                        <xsl:when test="@type= 'String'">
                            <Type>xs:string</Type>
                            <StringQualifiers>
                                <Length>150</Length>
                                <AllowedLength>Variable</AllowedLength>
                            </StringQualifiers>
                        </xsl:when>
                        <xsl:when test="@type='Date'">
                            <Type>xs:dateTime</Type>
                            <DateQualifiers>
                                <DateFractions>DateTime</DateFractions>
                            </DateQualifiers>
                        </xsl:when>
                        <xsl:when test="@type='Decimal' or @type='Currency'">
                            <Type>xs:decimal</Type>
                            <NumberQualifiers>
                                <Digits>20</Digits>
                                <FractionDigits>4</FractionDigits>
                                <AllowedSign>Any</AllowedSign>
                            </NumberQualifiers>
                        </xsl:when>
                        <xsl:when test="@type='Integer'">
                            <Type>xs:decimal</Type>
                            <NumberQualifiers>
                                <Digits>20</Digits>
                                <FractionDigits>0</FractionDigits>
                                <AllowedSign>Any</AllowedSign>
                            </NumberQualifiers>
                        </xsl:when>             
                    </xsl:choose>
                </ValueType>
            </column>
        </xsl:for-each>
        <!-- data -->
        <xsl:for-each select="scheme/data/o">
            <row>
                <xsl:for-each select=".//f">
                    <Value>
                        <xsl:value-of select="."/>
                    </Value>
                </xsl:for-each>
            </row>
        </xsl:for-each>
    </ValueTable>
</xsl:template>

</xsl:stylesheet>