代码之家  ›  专栏  ›  技术社区  ›  Nim

跳过已“访问”节点的xslt

  •  8
  • Nim  · 技术社区  · 14 年前

    不确定这是否可能,而不必经过几次传递,但无论如何我都会问(我的XSL有点生疏)

    我有一个XML文档,其中包含以下节点:

    <structures>
     <structure id="STRUCT_A">
       <field idref="STRUCT_B" name="b"/>
       <field idref="STRUCT_C" name="c"/>
       <field idref="FIELD_D" name="d"/>
     </structure>
    
     <structure id="STRUCT_B">
       <field idref="STRUCT_C" name="c"/>
       <field idref="FIELD_E" name="e"/>
     </structure>
    
     <structure id="STRUCT_C">
       <field idref="FIELD_E" name="e"/>
       <field idref="FIELD_F" name="f"/>
       <field idref="FIELD_G" name="g"/>
     </structure>
    </structures>
    

    (真正的文件包含许多相互依赖的结构标记,没有一个是循环的!)

    我想做的是生成一些文本(在这种情况下是C++) struct s) ,最明显的要求是 结构 s、 所以我的理想输出是

    struct STRUCT_C
    {
      FIELD_E e;
      FIELD_F f;
      FIELD_G g;
    };
    
    struct STRUCT_B
    {
      STRUCT_C c;
      FIELD_E e;
    };
    
    struct STRUCT_A
    {
      STRUCT_B b;
      STRUCT_C c;
      FIELD_D d;
    };
    

    我知道我可以使用forward声明,这意味着顺序无关紧要,但是问题是,在结构中会有内联的“处理”代码,它们需要真正的定义。

    到目前为止我能检测到 structure 具有任何依赖项,具有以下xsl位:

    <xsl:for-each select="descendant::*/@idref">
      <xsl:variable name="name" select="."/>
      <xsl:apply-templates select="//structure[@id = $name]" mode="struct.dep"/> 
    </xsl:for-each>
    

    (这发生在 <xsl:template match="structure"> )

    现在,理论上,我可以遵循这个依赖“链”并生成 结构 首先是每个条目,然后是我当前所在的条目,但是正如您所想象的,这会生成许多相同结构的副本,这是一个痛苦的过程。。

    有什么办法可以避免复制吗?基本上,一旦一个结构被访问,如果我们再次访问,就不用麻烦输出它的代码了。。。我不需要完整的xslt来完成这项工作(除非它很简单!),但任何关于方法的想法。。。

    如果没有,理论上我可以 结构 用一个 #ifdef / #define / #endif 注意编译器只使用第一个定义,但是这真的很糟糕!:(

    (注意:xslt 1.0,linux上的xsltproc:Using libxml 20623,libxslt 10115和libexslt 812)

    3 回复  |  直到 14 年前
        1
  •  7
  •   Dimitre Novatchev    14 年前

    这种转变 :

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:variable name="vLeafs" select="/*/structure[not(field/@idref = /*/structure/@id)]"/>
    
     <xsl:template match="/*">
      <xsl:apply-templates select="$vLeafs[1]">
       <xsl:with-param name="pVisited" select="'|'"/>
      </xsl:apply-templates>
    
     </xsl:template>
    
     <xsl:template match="structure">
       <xsl:param name="pVisited"/>
    
    struct <xsl:value-of select="@id"/>
    {<xsl:text/>
      <xsl:apply-templates/>
    };
      <xsl:variable name="vnewVisited"
           select="concat($pVisited, @id, '|')"/>
      <xsl:apply-templates select=
      "../structure[not(contains($vnewVisited, concat('|', @id, '|')))
                  and
                    not(field/@idref
                               [not(contains($vnewVisited, concat('|', ., '|')) )
                              and
                               . = ../../../structure/@id
                               ]
                       )
                   ] [1]
      ">
       <xsl:with-param name="pVisited" select="$vnewVisited"/>
      </xsl:apply-templates>
     </xsl:template>
    
     <xsl:template match="field">
      <xsl:value-of select="concat('&#xA;   ', @idref, ' ', @name, ';')"/>
     </xsl:template>
    </xsl:stylesheet>
    

    当应用于所提供的XML文档时 :

    <structures>
     <structure id="STRUCT_A">
       <field idref="STRUCT_B" name="b"/>
       <field idref="STRUCT_C" name="c"/>
       <field idref="FIELD_D" name="d"/>
     </structure>
    
     <structure id="STRUCT_B">
       <field idref="STRUCT_C" name="c"/>
       <field idref="FIELD_E" name="e"/>
     </structure>
    
     <structure id="STRUCT_C">
       <field idref="FIELD_E" name="e"/>
       <field idref="FIELD_F" name="f"/>
       <field idref="FIELD_G" name="g"/>
     </structure>
    </structures>
    

    产生想要的正确结果 :

    struct STRUCT_C
    {
       FIELD_E e;
       FIELD_F f;
       FIELD_G g;
    };
    
    
    struct STRUCT_B
    {
       STRUCT_C c;
       FIELD_E e;
    };
    
    
    struct STRUCT_A
    {
       STRUCT_B b;
       STRUCT_C c;
       FIELD_D d;
    };
    

    解释 : structure 元素被一个一个地严格处理。在任何时候我们处理第一个 结构 元素 id 尚未在中注册 pVisited 参数,它没有 field/@idref 尚未包含在 变视的 参数和引用现有的 结构 元素。

        2
  •  3
  •   user357812user357812    14 年前

    只是为了好玩,其他方法(逐级)和ussing键:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="text"/>
        <xsl:key name="kStructById" match="structure" use="@id"/>
        <xsl:key name="kStructByIdref" match="structure" use="field/@idref"/>
        <xsl:template match="/">
            <xsl:param name="pParents" select="/.."/>
            <xsl:param name="pChilds"
                       select="structures/structure[not(key('kStructById',
                                                            field/@idref))]"/>
            <xsl:variable name="vParents" select="$pParents|$pChilds"/>
            <xsl:variable name="vChilds"
                          select="key('kStructByIdref',$pChilds/@id)
                                     [count(key('kStructById',
                                                 field/@idref) |
                                            $vParents) =
                                      count($vParents)]"/>
            <xsl:apply-templates select="$pChilds"/>
            <xsl:apply-templates select="current()[$vChilds]">
                <xsl:with-param name="pParents" select="$vParents"/>
                <xsl:with-param name="pChilds" select="$vChilds"/>
            </xsl:apply-templates>
        </xsl:template>
        <xsl:template match="structure">
            <xsl:value-of select="concat('struct ',@id,'&#xA;{&#xA;')"/>
            <xsl:apply-templates/>
            <xsl:text>};&#xA;</xsl:text>
        </xsl:template>
        <xsl:template match="field">
            <xsl:value-of select="concat('&#x9;',@idref,' ',@name,';&#xA;')"/>
        </xsl:template>
    </xsl:stylesheet>
    

    输出:

    struct STRUCT_C
    {
        FIELD_E e;
        FIELD_F f;
        FIELD_G g;
    };
    struct STRUCT_B
    {
        STRUCT_C c;
        FIELD_E e;
    };
    struct STRUCT_A
    {
        STRUCT_B b;
        STRUCT_C c;
        FIELD_D d;
    };
    
        3
  •  2
  •   LarsH    14 年前

    哦,这比一开始看起来更复杂。+一个好问题。

    我认为,在XSLT 1.0中实现这一点的最佳方法是在将模板应用于结构时传递一个累加参数。参数(称为“$visited structures”)是一个用空格分隔的已处理结构名称列表。

    最新消息:终于拿到这个了。:-)

    在用于处理结构的模板中,检查$visited structures中是否已列出此结构所依赖的任何其他结构。如果不是,则生成此结构的代码,并在模板上递归,选择下一个未访问的结构,将当前结构名称附加到$visited structures参数。否则,不要为结构生成代码,而是在模板上递归,选择第一个依赖结构,未经修改传递$visited structures参数。

    这是密码。。。

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
       version="1.0">
       <xsl:key name="structuresById" match="/*/structure" use="@id" />
    
       <xsl:template match="structures">
          <xsl:apply-templates select="structure[1]" >
             <!-- a space-delimited list of id's of structures already processed, with space
                at beginning and end. Could contain duplicates. -->
             <xsl:with-param name="visited-structures" select="' '"/>
          </xsl:apply-templates>
       </xsl:template>
    
       <xsl:template match="structure">
          <xsl:param name="visited-structures" select="' '" />  
          <xsl:variable name="dependencies" select="key('structuresById', field/@idref)
                         [not(contains($visited-structures, @id))]"/>
          <xsl:choose>
             <xsl:when test="$dependencies">
                <xsl:apply-templates select="$dependencies[1]">
                   <xsl:with-param name="visited-structures" select="$visited-structures"/>
                </xsl:apply-templates>            
             </xsl:when>
             <xsl:otherwise>
                <!-- Now generate code for this structure ... ... -->
    struct <xsl:value-of select="@id"/>
    {
    <xsl:apply-templates select="field"/>};
                <xsl:variable name="new-visited" select="concat(' ', @id, $visited-structures)"/>
                <xsl:apply-templates select="/*/structure[not(contains($new-visited, @id))][1]" >
                   <xsl:with-param name="visited-structures" select="$new-visited"/>
                </xsl:apply-templates>
             </xsl:otherwise>
          </xsl:choose>      
       </xsl:template>
    
       <xsl:template match="field">
          <xsl:value-of select="concat('  ', @idref, ' ', @name, ';&#xa;')"/>      
       </xsl:template>
    
    </xsl:stylesheet>
    

    以及输出:

    <?xml version="1.0" encoding="utf-8"?>
    
    struct STRUCT_C
    {
      FIELD_E e;
      FIELD_F f;
      FIELD_G g;
    };
    
    
    struct STRUCT_B
    {
      STRUCT_C c;
      FIELD_E e;
    };
    
    
    struct STRUCT_A
    {
      STRUCT_B b;
      STRUCT_C c;
      FIELD_D d;
    };