在现有元素上应用属性集

Posted

技术标签:

【中文标题】在现有元素上应用属性集【英文标题】:Apply attribute-sets on existing elements 【发布时间】:2021-08-15 20:58:22 【问题描述】:

使用具有相对扁平的层次树结构的导入 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>

【问题讨论】:

&lt;xsl:for-each select="."&gt; 用作xsl:template match 的子代似乎毫无意义。 【参考方案1】:

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

  <!-- [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>

【讨论】:

【参考方案2】:

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

<?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]-stage,您也可以像这样保存它:

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

【讨论】:

以上是关于在现有元素上应用属性集的主要内容,如果未能解决你的问题,请参考以下文章

如何向 TypeScript/JSX 中的现有 HTML 元素添加属性?

使用基于属性的过滤子元素集获取核心数据实体

为每个查询的元素添加属性集

Zombie.js 无法访问 DOM 元素的数据集属性

jQuery选择所有有标题的元素

在 VueJS 2 组件模板外使用带有 dom 元素的 ref 属性