xslt跳过已经"访问过"的节点

Nim*_*Nim 8 xslt

不确定这是否可行而不必经过多次通过,但无论如何我都会问(我的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>
Run Code Online (Sandbox Code Playgroud)

(真正的文件包含许多相互依赖的结构标签,其中没有一个是圆形的!)

我想要做的是生成一些文本(在这种情况下是C++ struct),明显的要求是structs 的顺序,所以我的理想输出将是

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;
};
Run Code Online (Sandbox Code Playgroud)

我知道我可以使用前向声明,这意味着顺序无关紧要,但问题是结构中会内嵌"处理"代码,并且它们需要存在真正的定义.

到目前为止,我可以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>
Run Code Online (Sandbox Code Playgroud)

(这发生在一个内部<xsl:template match="structure">)

现在,理论上,我可以遵循这个依赖关系"链"并struct首先为每个条目生成s,然后是我当前所在的那个,但是你可以想象,这会产生大量相同结构的副本 - 这是一个痛..

反正有没有副本?基本上,一旦访问了一个结构,如果我们再次访问,不要为它输出代码而烦恼...我不需要完整的xslt来做这个(除非它是微不足道的!),但只是关于方法的任何想法...

如果没有,我理论上可以struct使用#ifdef/ #define/ #endifguard 包装,以便编译器只使用第一个定义,但这真的很棒!:(

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

Dim*_*hev 7

这种转变:

<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>
Run Code Online (Sandbox Code Playgroud)

当应用于提供的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>
Run Code Online (Sandbox Code Playgroud)

产生想要的,正确的结果:

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;
};
Run Code Online (Sandbox Code Playgroud)

说明: structure元素严格逐个处理.在任何时候,我们处理尚未在参数中注册的第一个structure元素,该元素id没有pVisited参数中尚未存在的field/@idref值,pVisited并且引用现有structure元素.