xslt 跳过已经“访问过"节点 [英] xslt to skip already "visited" nodes

查看:22
本文介绍了xslt 跳过已经“访问过"节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

不确定这是否可能无需经过多次传递,但我还是会问(我的 XSL 有点生疏)

not sure if this is possible without having to go through several passes, but I'll ask anyway (my XSL is a little rusty)

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

I have an XML document, which contains nodes as follows:

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

(真实文件中包含很多相互依赖的结构标签,没有一个是循环的!)

(The real file contains lots of structure tags which interdependencies, none of which are circular!)

我想要做的是生成一些文本(在本例中为 C++ structs),并且明显的要求是 structs 的顺序,所以我的理想的输出是

What I want to do is to generate some text (in this case C++ structs), and the obvious requirement is the order of the structs, so my ideal output would be

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;
};

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

I know I could use forward declarations and that would mean that the order doesn't matter, however the problem is that there will be "processing" code inline in the structures, and they would require the real definition to be present.

到目前为止,我可以通过以下 xsl 来检测 structure 是否有任何依赖项:

So far I can detect to see if a structure has any dependencies, with the following bit of 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>

(这发生在 中)

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

Now, theoretically, I could then follow this dependency "chain" and generate the structs for each entry first, then the one that I am currently at, however as you can imagine, this generates lot's of copies of the same structure - which is a pain..

有没有办法避免复制?基本上,一旦访问了一个结构,并且如果我们再次访问,就不必费心输出它的代码……我不需要完整的 xslt 来执行此操作(除非它很琐碎!),但只需要有关方法的任何想法...

Is there anyway to avoid the copies? Basically, once a structure has been visited, and if we visit again, not to bother outputting the code for it... I don't need the full xslt to do this (unless it's trivial!), but just any ideas on approaches...

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

If there isn't, I could in theory wrap the struct with a #ifdef/#define/#endif guard so that the compiler only uses the first definition, however this is REALLY NASTY! :(

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

(NOTES: xslt 1.0, xsltproc on linux: Using libxml 20623, libxslt 10115 and libexslt 812)

推荐答案

这种转变:

<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 元素严格一一处理.任何时候我们都会处理第一个 structure 元素,其 id 尚未在 pVisited 参数中注册,并且没有 field/@idref 值不在 pVisited 参数中,并且引用了现有的 structure 元素.

Explanation: structure elements are processed strictly one by one. At any time we process the first structure element whose id isn't yet registered in the pVisited parameter and that has no field/@idref value that isn't already in the pVisited parameter and refers to an existing structure element.

这篇关于xslt 跳过已经“访问过"节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆