不确定这是否可行而不必经过多次通过,但无论如何我都会问(我的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)
这种转变:
<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('
 ', @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元素.