在现有元素上应用属性集

Apply attribute-sets on existing elements

使用具有相对扁平的分层树结构的导入 JSON 数据。 在当前代码中,我在创建元素期间应用属性集。

是否可以先创建所有元素,然后再应用属性集? 似乎“use-attribute-sets”是一个属性,因此需要添加到元素上才能工作。

我当前的代码中没有错误消息。

我只是想看看是否可以按如下所述的特定顺序执行操作。 之所以计划更改,是为了处理更大的数据量,因此首先执行元素的解析和创建,然后才执行通过属性集添加属性的统一方式。

我的序列:

[1] Create attribute sets. 
[2] Group element names. 
[3] Parse JSON to XML map. 
[4] Build element, using attribute-sets and extract key value

我要执行的序列:

[1] Create attribute sets (same as above).
[2] Group element names (same as above).
[3] Parse JSON to XML map (same as above).
[4] Build element names with corresponding key (split of above bullet 4).
[5] Add attribute-set based on template match in the code (split of above bullet 4).

JSON:

<data>
{
  "store": {
    "pencils": 43,
    "milk": 21,
    "rulers": 12,
    "beer": 17
  }
}
</data>

XSL:

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

<xsl:transform version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:inventory="http://www.example.org/1"
  xmlns:item="http://www.example.org/2"
  expand-text="yes"
>

  <xsl:output method="xml" indent="yes"/>
  <xsl:mode on-no-match="shallow-skip"/>

  <!-- [1] Create attribute sets -->

  <xsl:attribute-set name="group-office">
    <xsl:attribute name="contextRef">office</xsl:attribute>
  </xsl:attribute-set>

  <!-- [2] Group element names-->

  <xsl:param name="group-office">pencils, rulers</xsl:param>
  <xsl:param name="attributes-for-group-office" select="tokenize($group-office, ',\s*')"/>

  <!-- [3] Parse JSON to XML -->

  <xsl:template match="data">
      <inventory:store>
        <xsl:apply-templates select="json-to-xml(.)/*"/>
      </inventory:store>
  </xsl:template>

  <!-- [4] Build element, using attribute-sets and extract key value -->

  <xsl:template match="*[@key = 'store']/*[@key = $attributes-for-group-office]">
    <xsl:for-each select=".">
      <xsl:element name="item:{@key}" use-attribute-sets="group-office">
        <xsl:value-of select="text()"/>
      </xsl:element>
    </xsl:for-each>
  </xsl:template>

</xsl:transform>

结果 (当前以及代码顺序更改后的样子):

<?xml version="1.0" encoding="UTF-8"?>
<inventory:store xmlns:inventory="http://www.example.org/1"
                 xmlns:item="http://www.example.org/2">
   <item:pencils contextRef="office">43</item:pencils>
   <item:rulers contextRef="office">12</item:rulers>
</inventory:store>

您可以使用这样的模式(添加了一些元素以使所需的阶段更加清晰):

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

<xsl:transform version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:inventory="http://www.example.org/1"
  xmlns:item="http://www.example.org/2"
  expand-text="yes"
  >
  
  <xsl:output method="xml" indent="yes"/>
  <xsl:mode on-no-match="shallow-skip"/>
  <xsl:mode name="useAttributeSet" on-no-match="shallow-skip"/>
  
  <!-- [1] Create attribute sets -->
  
  <xsl:attribute-set name="group-office">
    <xsl:attribute name="contextRef">office</xsl:attribute>
  </xsl:attribute-set>
  
  <!-- [2] Group element names-->
  
  <xsl:param name="group-office">pencils, rulers</xsl:param>
  <xsl:param name="attributes-for-group-office" select="tokenize($group-office, ',\s*')"/>
  
  <!-- [3] Parse JSON to XML -->
  
  <xsl:template match="data">
    <xsl:variable name="withoutAttributeSets">
      <xsl:apply-templates select="json-to-xml(.)/*"/>
    </xsl:variable>
    <stages>
      <stage>
        <inventory:store>
          <xsl:copy-of select="$withoutAttributeSets"/>
        </inventory:store>
      </stage>
      <stage>
        <inventory:store>
          <xsl:apply-templates select="$withoutAttributeSets" mode="useAttributeSet"/>
        </inventory:store>
      </stage>
    </stages>
  </xsl:template>
  
  <!-- [4] Build element names with corresponding key (split of above bullet 4). -->
  
  <xsl:template match="*[@key = 'store']/*[@key = $attributes-for-group-office]">
    <xsl:for-each select=".">
      <xsl:element name="item:{@key}" >
        <xsl:value-of select="text()"/>
      </xsl:element>
    </xsl:for-each>
  </xsl:template>
  
  <!-- [5] Add attribute-set based on template match in the code (split of above bullet 4). -->

  <xsl:template match="*[local-name() = $attributes-for-group-office]" mode="useAttributeSet">
  <xsl:element name="{name()}" use-attribute-sets="group-office">
      <xsl:value-of select="text()"/>
    </xsl:element>
  </xsl:template>

</xsl:transform>

这将给出这个结果:

<?xml version="1.0" encoding="UTF-8"?>
<stages xmlns:inventory="http://www.example.org/1"
        xmlns:item="http://www.example.org/2">
   <stage>
      <inventory:store>
         <item:pencils>43</item:pencils>
         <item:rulers>12</item:rulers>
      </inventory:store>
   </stage>
   <stage>
      <inventory:store>
         <item:pencils contextRef="office">43</item:pencils>
         <item:rulers contextRef="office">12</item:rulers>
      </inventory:store>
   </stage>
</stages>

根据您的需要进行调整。

如果您想以其他方式重用此 [4] 阶段,您也可以这样保存它:

<xsl:result-document href="stage-4.xml">
  <inventory:store>
    <xsl:copy-of select="$withoutAttributeSets"/>
  </inventory:store>
</xsl:result-document>

这是一个相当人为的并且不是很容易的分离,因为你不能在不创建元素的情况下注入属性集,所以你所能做的就是,如果有帮助的话,写一个具有高优先级的模板 select 元素名称并将其传递给期望该名称作为参数的较低级元素,然后像以前一样进行实际工作,以创建具有属性集的元素:

  <!-- [4] Extract key value for element name -->

  <xsl:template match="*[@key = 'store']/*[@key = $attributes-for-group-office]" priority="10">
    <xsl:next-match>
      <xsl:with-param name="element-name" select="'item:' || @key"/>
    </xsl:next-match>
  </xsl:template>
  
  <!-- [5] Build element and add attribute-set based on template match in the code -->

  <xsl:template match="*[@key = 'store']/*[@key = $attributes-for-group-office]" priority="5">
    <xsl:param name="element-name" required="yes"/>
    <xsl:element name="{$element-name}" use-attribute-sets="group-office">
      <xsl:value-of select="text()"/>
    </xsl:element>
  </xsl:template>

我不确定分离是否有意义,但如果您真的想要两个单独的模板,目前我想不出其他任何东西。当然,Siebe 基于模式的临时树建议也是一种选择,但需要临时树;或者你可以使用上面的而不是依赖优先级来确保处理顺序使用模式并推送相同的节点,对我来说感觉就像以前一样人为和困难的分离:

  <!-- [4] Build element and extract key value -->

  <xsl:template match="*[@key = 'store']/*[@key = $attributes-for-group-office]">
    <xsl:apply-templates select="." mode="add-attribute-sets">
      <xsl:with-param name="element-name" select="'item:' || @key"/>
    </xsl:apply-templates>
  </xsl:template>
  
  <!-- [5] Build element and add attribute-set based on template match in the code -->

  <xsl:template match="*[@key = 'store']/*[@key = $attributes-for-group-office]" mode="add-attribute-sets">
    <xsl:param name="element-name" required="yes"/>
    <xsl:element name="{$element-name}" use-attribute-sets="group-office">
      <xsl:value-of select="text()"/>
    </xsl:element>
  </xsl:template>