首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在XSLT-2.0中,将空间分隔的标记从一个属性移动到另一个属性的最佳方法是什么?

在XSLT-2.0中,将空间分隔的标记从一个属性移动到另一个属性的最佳方法是什么?
EN

Stack Overflow用户
提问于 2020-06-26 15:27:02
回答 3查看 169关注 0票数 1

在XSLT-2.0中,我试图将空间分隔的标记从一个属性移动到另一个属性。例如,给定

代码语言:javascript
复制
<!-- SOURCE DOCUMENT -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <p class="foo"/>
    <p class="foo bar baz"/>
    <p class="foo bar baz" outputclass="BAR"/>
    <p class="foo bar baz" outputclass="BAR HELLO"/>
</root>

我需要将@class="foo“移动到@outputclass="FOO”,将@class="bar“移到@outputclass="BAR",如果源属性变为空,则删除它,如果存在目标属性,则删除它(简单的令牌集操作):

代码语言:javascript
复制
<!-- RESULTING DOCUMENT -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <p             outputclass="FOO"/>
    <p class="baz" outputclass="FOO BAR"/>
    <p class="baz" outputclass="FOO BAR"/>
    <p class="baz" outputclass="FOO BAR HELLO"/>
</root>

我想我已经把所有事情都解决了,除了真正的象征性移动部分。我往下走的每一个方向最终都会变得复杂和破碎,我觉得XSLT2.0肯定有一种简单的方法,而这正是我所缺少的。

到目前为止,我的情况如下:

代码语言:javascript
复制
<?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"
    xmlns:mine="mine:local"
    exclude-result-prefixes="xs"
    version="2.0">

    <!-- baseline identity transform -->
    <!-- (for non-elements - attributes, whitespace PCDATA, etc.)  -->
    <xsl:template match="@*|(node() except *)">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <!-- for element nodes, remap attributes then copy element -->
    <xsl:template match="*">
        <!-- get original attribute sequence -->
        <xsl:variable name="atts1" select="@*"/>

        <!-- use our function to remap two attribute tokens -->
        <xsl:variable name="atts2" select="mine:remap($atts1, 'class', 'foo', 'outputclass', 'FOO')"/>
        <xsl:variable name="atts3" select="mine:remap($atts2, 'class', 'bar', 'outputclass', 'BAR')"/>

        <!-- stuff updated attribute sequence into element -->
        <xsl:copy>
            <xsl:sequence select="$atts3"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

    <!-- remap  @from_att~="$from_token"  to  @to_att~="$to_token" -->
    <xsl:function name="mine:remap">
        <xsl:param name="orig_atts"/>
        <xsl:param name="from_att"/>
        <xsl:param name="from_token"/>
        <xsl:param name="to_att"/>
        <xsl:param name="to_token"/>

        <!-- ******** TOKEN-MOVING MAGIC!?! ******** -->

        <xsl:sequence select="$orig_atts"/>
    </xsl:function>
</xsl:stylesheet>

基本上,我需要弄清楚TOKEN-MOVING MAGIC!?!如何移动单个令牌(包括从“属性中删除空”)。我已经搜索了很多次了,但是我还没有看到这个特殊的问题。

编辑:要重新映射的属性的数量和名称可以是任意的,它们的值是区分大小写的。在我正在寻找的属性序列中重新映射单个值是mine:remap函数中的神奇之处。

编辑:使用函数处理属性修改的原因是,我们有许多不同的令牌映射可应用于不同的文件,我希望允许不熟悉XSLT的用户轻松地根据他们的需要调整这些映射。我想不出如何用基于模板匹配的方法来提供类似的泛化。

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-07-05 05:23:18

这里是一个简短的XSLT2.0解决方案(仅26行):

代码语言:javascript
复制
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="p/@class[tokenize(., ' ') = ('foo', 'bar')]">
    <xsl:if test="tokenize(., ' ')[not(. = ('foo', 'bar'))]">
        <xsl:attribute name="class" 
             select="string-join(tokenize(., ' ')[not(. = ('foo', 'bar'))], ' ')"/>
    </xsl:if>
    <xsl:attribute name="outputclass" select=
      "upper-case(string-join(
                   (
                    tokenize(., ' ')[. = ('foo', 'bar')],
                    tokenize(../@outputclass, ' ')
                                 [not(lower-case(.) = tokenize(current(), ' '))]
                    ),
                    ' '
                              )
                  )"/>
  </xsl:template>
  
  <xsl:template match="p/@outputclass[../@class[tokenize(., ' ') = ('foo', 'bar')]]"/>
</xsl:stylesheet>

当此转换应用于所提供的XML文档时,则为。

代码语言:javascript
复制
<root>
    <p class="foo"/>
    <p class="foo bar baz"/>
    <p class="foo bar baz" outputclass="BAR"/>
    <p class="foo bar baz" outputclass="BAR HELLO"/>
</root>

想要的,正确的结果产生

代码语言:javascript
复制
<root>
    <p outputclass="FOO"/>
    <p class="baz" outputclass="FOO BAR"/>
    <p class="baz" outputclass="FOO BAR"/>
    <p class="baz" outputclass="FOO BAR HELLO"/>
</root>

更新

下面是几乎所有参数化的转换,正如OP在注释中所要求的那样,只有32行:

代码语言:javascript
复制
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:param name="pfromName" select="'class'"/>
 <xsl:param name="ptoName" select="'outputclass'"/>
 <xsl:param name="pTokens" select="'foo', 'bar'"/>
 <xsl:param name="pnewNames" select="'FOO', 'BAR'"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="p/@*[name() = $pfromName][tokenize(., ' ') = $pTokens]">
    <xsl:if test="tokenize(., ' ')[not(. = $pTokens)]">
        <xsl:attribute name="{$pfromName}"
             select="string-join(tokenize(., ' ')[not(. = $pTokens)], ' ')"/>
    </xsl:if>
    <xsl:attribute name="{$ptoName}" select=
      "upper-case(string-join(
                   (
                    tokenize(., ' ')[. = $pTokens],
                    tokenize(../@*[name()=$ptoName], ' ')
                                 [not(lower-case(.) = tokenize(current(), ' '))]
                    ),
                    ' '
                              )
                  )"/>
  </xsl:template>

  <xsl:template 
    match="p/@*[name()=$ptoName][../@*[name()=$pfromName][tokenize(., ' ') = $pTokens]]"/>
</xsl:stylesheet>

Update2

这里是一个完全参数化的XSLT2.0转换(不使用upper-case()lower-case()函数),只有37行:

代码语言:javascript
复制
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:param name="pfromName" select="'class'"/>
 <xsl:param name="ptoName" select="'outputclass'"/>
 <xsl:param name="pTokens" select="'foo', 'bar'"/>
 <xsl:param name="pnewNames" select="'FOO', 'BAR'"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="p/@*[name() = $pfromName][tokenize(., ' ') = $pTokens]">
    <xsl:if test="tokenize(., ' ')[not(. = $pTokens)]">
        <xsl:attribute name="{$pfromName}"
             select="string-join(tokenize(., ' ')[not(. = $pTokens)], ' ')"/>
    </xsl:if>
    <xsl:attribute name="{$ptoName}" select=
      "string-join(
                   distinct-values(
                            (for $token in tokenize(., ' ')[. = $pTokens],
                                    $n in 1 to count($pTokens),
                                    $ind in $n[$token eq $pTokens[$n]]
                                  return $pnewNames[$ind]
                             ,
                              tokenize(../@*[name()=$ptoName], ' ')
                              )
                                    ),
                    ' '
                    )
                  "/>
  </xsl:template>

  <xsl:template
  match="p/@*[name()=$ptoName][../@*[name()=$pfromName][tokenize(., ' ') = $pTokens]]"/>
</xsl:stylesheet>
票数 1
EN

Stack Overflow用户

发布于 2020-06-27 10:31:16

在下面的示例中,我尝试尽可能多地委托模板:

代码语言:javascript
复制
<?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="#all"
    version="3.0">
    
  <xsl:param name="tokens" as="xs:string*"
    select="'foo', 'bar'"/>
    
  <xsl:param name="collation" as="xs:string">http://www.w3.org/2005/xpath-functions/collation/html-ascii-case-insensitive</xsl:param>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="*[@class][exists($tokens[contains-token(current()/@class, ., $collation)])]">
      <xsl:copy>
          <xsl:variable name="new-att" as="attribute()">
              <xsl:attribute name="outputclass"/>
          </xsl:variable>
          <xsl:apply-templates select="@*, $new-att[not(current()/@outputclass)]">
              <xsl:with-param name="tokens-found" 
                select="$tokens[contains-token(current()/@class, ., $collation)]"/>
          </xsl:apply-templates>
          <xsl:apply-templates/>
      </xsl:copy>
  </xsl:template>
  
  <xsl:template match="@class">
      <xsl:param name="tokens-found"/>
      <xsl:variable name="remaining-tokens" select="tokenize(., ' ')[not(. = $tokens-found)]"/>
      <xsl:if test="exists($remaining-tokens)">
          <xsl:attribute name="{name()}" select="$remaining-tokens"/>
      </xsl:if>
  </xsl:template>
  
  <xsl:template match="@outputclass">
      <xsl:param name="tokens-found"/>
      <xsl:variable name="new-tokens" select="$tokens-found[not(contains-token(current(), ., $collation))]"/>
      <xsl:attribute name="{name()}" select="$new-tokens, ."/>
  </xsl:template>
  
</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bEzkTcx/1

我还没有实现要移动的令牌的大写转换,我想应该很容易添加。

代码在XPath 3和函数https://www.w3.org/TR/xpath-functions/#func-contains-token中使用XSLT3,但是它在规范中有一个定义,您可以在用户定义的XSLT 2函数中使用它。当然,使用xsl:mode来声明身份转换也很容易,而是通过拼写来实现。

XSLT 3可在Saxon9.8或更高版本中用于Java和.NET,Saxon用于C/C++,绑定用于PHP和Python,以及Saxon-JS2在现代web浏览器和Node.js中可用。

票数 0
EN

Stack Overflow用户

发布于 2020-06-30 20:47:55

下面是我最后得到的mine:remap()函数:

代码语言:javascript
复制
<!-- remap  @from_att~="$from_token"  to  @to_att~="$to_token" -->
<xsl:function name="mine:remap">
    <xsl:param name="orig_atts" as="attribute()*"/>
    <xsl:param name="from_att"/>
    <xsl:param name="from_token"/>
    <xsl:param name="to_att"/>
    <xsl:param name="to_token"/>

    <!-- get tokenized list of values of "from" attributes -->
    <xsl:variable name="from_att_values" select="tokenize($orig_atts[name() = $from_att], ' ')"/>

    <xsl:choose>
        <!-- does the "from" attribute contain our value to replace? -->
        <xsl:when test="$from_att_values = $from_token">

            <!-- if so, iterate through attributes to preserve their order -->
            <xsl:for-each select="$orig_atts">
                <xsl:choose>
                    <!-- if "from" and "to" attributes are the same, replace $from_token with $to_token in-place -->
                    <xsl:when test="(name(.) = $from_att) and ($from_att = $to_att)">
                        <xsl:attribute name="{name(.)}" select="for $t in $from_att_values
                            return ($t[$t != $from_token], $to_token[$t = $from_token])"/>                        
                    </xsl:when>
                    <!-- if "from" attribute, define with $from_token value removed -->
                    <xsl:when test="name(.) = $from_att">
                        <xsl:variable name="new_from_att_values" select="$from_att_values[not(. = $from_token)]"/>
                        <xsl:if test="count($new_from_att_values) > 0">
                            <xsl:attribute name="{$from_att}" select="$new_from_att_values"/>
                        </xsl:if>
                    </xsl:when>
                    <!-- if "to" attribute, define with $to_token value added -->
                    <xsl:when test="name(.) = $to_att">
                        <xsl:attribute name="{$to_att}" select="distinct-values((tokenize(., ' '), $to_token))"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
            <!-- if there was no "from" attribute to modify above, create it here -->
            <xsl:if test="not($orig_atts[name() = $to_att])">
                <xsl:attribute name="{$to_att}" select="$to_token"/>
            </xsl:if>
        </xsl:when>

        <!-- if not, return original attributes -->
        <xsl:otherwise>
            <xsl:sequence select="$orig_atts"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:function>

我遍历这些属性以保持其顺序,然后使用xsl:remove处理from (移除一个令牌)、to (添加一个令牌)或其他(复制)属性。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62597965

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档