不确定这是否可行而不必经过多次通过,但无论如何我都会问(我的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),明显的要求是struct
的顺序,所以我的理想输出是
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
是否有任何依赖关系,以及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">
)
现在,理论上,我可以遵循这个依赖“链”并首先为每个条目生成struct
s,然后生成我当前所在的条目,但是你可以想象,这会生成大量的副本相同的结构 - 这是一种痛苦..
无论如何都要避免副本?基本上,一旦访问了一个结构,如果我们再次访问,不要为它输出代码而烦恼...我不需要完整的xslt来做这个(除非它是微不足道的!),但只是关于方法的任何想法...
如果没有,我理论上可以用struct
/ #ifdef
/ #define
保护包裹#endif
,以便编译器只使用第一个定义,不过这真是太棒了! :(
(注意:xslt 1.0,linux上的xsltproc:使用libxml 20623,libxslt 10115和libexslt 812)
答案 0 :(得分: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('
 ', @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
中{1}}参数,指的是现有的structure
元素。
答案 1 :(得分:3)
只是为了好玩,其他方法(逐级)和使用键:
<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,'
{
')"/>
<xsl:apply-templates/>
<xsl:text>};
</xsl:text>
</xsl:template>
<xsl:template match="field">
<xsl:value-of select="concat('	',@idref,' ',@name,';
')"/>
</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;
};
答案 2 :(得分:2)
我认为在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, ';
')"/>
</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;
};