首页 文章

生成XPATH从XML文档到属性级别

提问于
浏览
2

我有以下XSLT,它为已填充的XML文件中的每个元素创建XPATH .

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no"/>
<xsl:template match="*[not(*)]">
    <xsl:for-each select="ancestor-or-self::*">
        <xsl:value-of select="concat('/', name())"/>
        <xsl:if test="count(preceding-sibling::*[name() = name(current())]) != 0">
            <xsl:value-of select="concat('[', count(preceding-sibling::*[name() = name(current())]) + 1, ']')"/>
        </xsl:if>
    </xsl:for-each>
    <xsl:text>&#xA;</xsl:text>
    <xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="*">
    <xsl:apply-templates select="*"/>
</xsl:template>
</xsl:stylesheet>

我想扩展它,以便在'attribue'级别生成一个唯一的行 . 目前,即使它具有多个属性,也会为给定元素生成一个XPATH语句 . 如果一个元素有3个属性,我希望每个生成3个XPATH行 . 任何想法赞赏! :)谢谢

更新

嗨伊恩,

非常感谢您的帮助 .

我需要一个增强功能,我认为相当棘手?

如果一个元素有一个xsi:type属性(即另一个类型的扩展),我需要在XPATH中的元素名称中考虑它,而不是作为属性本身(因为它是一个特殊的/保留的XSD属性)

例如对于

<a>
   <b xsi:type="c" attribute1="at1"/>
</a>

我需要:

/a/b[xsi:type="c"]/@attribute1

而不是

/a/b/@type

它目前正在 生产环境 ?这可能吗?

谢谢!

UPDATE

非常感谢!请求最后一个请求 . :)你可以调整它来添加XML中的元素或属性VALUE,从XPATH部分用逗号分隔分隔.....所以是[XPATH],[VALUE]

1 回答

  • 0

    我将生成代码的路径拉出到另一个模板中,然后从模板中调用元素和属性:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
      <xsl:output method="text" indent="no" />
    
      <xsl:template mode="path" match="*">
        <xsl:for-each select="ancestor-or-self::*">
          <xsl:value-of select="concat('/', name())" />
          <xsl:if test="count(preceding-sibling::*[name() = name(current())]) != 0">
            <xsl:value-of select="concat('[',
              count(preceding-sibling::*[name() = name(current())]) + 1, ']')" />
          </xsl:if>
        </xsl:for-each>
      </xsl:template>
    
      <xsl:template match="*">
        <xsl:if test="not(*)">
          <!-- this element has no children, print its path -->
          <xsl:apply-templates mode="path" select="." />
          <xsl:text>&#xa;</xsl:text>
        </xsl:if>
        <xsl:apply-templates select="@*|*" />
      </xsl:template>
    
      <xsl:template match="@*">
        <!-- for an attribute, print the path to its containing element ... -->
        <xsl:apply-templates mode="path" select=".." />
        <!-- ... followed by "/@attributename" -->
        <xsl:value-of select="concat('/@', name(), '&#xa;')" />
      </xsl:template>
    </xsl:stylesheet>
    

    XPath数据模型很方便,虽然属性节点不被认为是其主机元素节点的子节点,但该元素被认为是其属性节点的父节点 .


    更新:要处理 xsi:type 要求,您应该能够将该逻辑添加到当前的兄弟计数代码中,以不同方式处理类型化元素 . 要添加值,您需要在适当的位置使用合适的 value-of .

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <xsl:output method="text" indent="no" />
    
      <xsl:template mode="path" match="*">
        <xsl:for-each select="ancestor-or-self::*">
          <xsl:value-of select="concat('/', name())" />
          <!-- for typed elements, include the type in the path -->
          <xsl:if test="@xsi:type">
            <xsl:text>[@xsi:type = '</xsl:text>
            <xsl:value-of select="@xsi:type" />
            <xsl:text>']</xsl:text>
          </xsl:if>
          <xsl:if test="count(preceding-sibling::*[name() = name(current()) and 
                @xsi:type = current()/@xsi:type]) != 0">
            <xsl:value-of select="concat('[',
              count(preceding-sibling::*[name() = name(current()) and 
                @xsi:type = current()/@xsi:type]) + 1, ']')" />
          </xsl:if>
        </xsl:for-each>
      </xsl:template>
    
      <xsl:template match="*">
        <xsl:if test="not(*)">
          <!-- this element has no children, print its path -->
          <xsl:apply-templates mode="path" select="." />
          <xsl:text>,</xsl:text>
          <xsl:value-of select="."/>
          <xsl:text>&#xa;</xsl:text>
        </xsl:if>
        <xsl:apply-templates select="@*|*" />
      </xsl:template>
    
      <xsl:template match="@*">
        <!-- for an attribute, print the path to its containing element ... -->
        <xsl:apply-templates mode="path" select=".." />
        <!-- ... followed by "/@attributename" -->
        <xsl:value-of select="concat('/@', name(), ',', ., '&#xa;')" />
      </xsl:template>
    </xsl:stylesheet>
    

    由于空节点集的字符串值是空字符串,因此对没有 xsi:type 的元素执行 @xsi:type = current()/@xsi:type 是安全的并且将生成正确的结果 .

    您可能还想添加

    <xsl:template match="@xsi:type" />
    

    如果您不想生成类型属性的路径 .

    值得注意的是,类型逻辑和计数是正交的 - 给定

    <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <a xsi:type="xs:string">aaa</a>
      <a xsi:type="xs:integer">1</a>
      <a xsi:type="xs:string">bbb</a>
      <a xsi:type="xs:integer">2</a>
    </root>
    

    你会得到的路径

    /root/a[@xsi:type = 'xs:string'],aaa
    /root/a[@xsi:type = 'xs:integer'],1
    /root/a[@xsi:type = 'xs:string'][2],bbb
    /root/a[@xsi:type = 'xs:integer'][2],2
    

    使用每种类型的计数(这是正确的,XPath将选择正确的命名空间绑定) .

相关问题