我们将CruiseControl用于构建服务器。它使用MSBuild编译我们的应用程序,并使用自己的XML记录器,它会像下面的XML一样:
<project name="CI">
<target name="CompileApp">
<project name="Project1.csproj">
<target name="build">
<error>Compilation error one!</error>
</target>
<target name="BeforeBuild">
<project name="Project2.csproj">
<target name="build">
<error>Compilation error two!</error>
</target>
</project>
</target>
</project>
</target>
</project>
我想将其转换为输出每个项目错误的报告。我不想报告其他项目中的错误。
Project "Project1.csproj": 1 error(s)
Error(s):
Compilation error one!
Project "Project2.csproj": 1 error(s)
Error(s):
Compilation error two!
这是我得到的最接近的,但这不对。显示project1的错误时,它不会过滤掉project2的错误。
<xsl:template>
<xsl:variable select="//project[.//error]" name="projects.with.errors" />
<xsl:apply-templates select="$projects.with.errors" />
</xsl:template>
<xsl:template match="project">
<xsl:variable select="./*[not(project)]//error" name="errors" />
<xsl:if test="count($errors) > 1">
<!-- display errors -->
</xsl:if>
</xsl:template>
如何过滤掉与当前项目节点具有不同项目祖先的任何错误节点?即,我怎样才能选择没有项目祖先的后代错误节点?
错误节点可以包含任意数量的父元素(通常但不总是)。
答案 0 :(得分:2)
此转化:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kProjectByName" match="project"
use="@name"/>
<xsl:key name="kErrByProject" match="error"
use="ancestor::project[1]/@name"/>
<xsl:template match="/">
<xsl:for-each select=
"//project
[generate-id()
=
generate-id(key('kProjectByName', @name)[1])
]
">
<xsl:variable name="vErrors" select=
"key('kErrByProject', @name)"/>
<xsl:if test="$vErrors">
Project <xsl:value-of select="@name"/><xsl:text>: </xsl:text>
<xsl:value-of select="count($vErrors)"/> errors.
Errors:
<xsl:for-each select="$vErrors">
<xsl:value-of select="."/>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
应用于提供的XML文档:
<project name="CI">
<target name="CompileApp">
<project name="Project1.csproj">
<target name="build">
<error>Compilation error one!</error>
</target>
<target name="BeforeBuild">
<project name="Project2.csproj">
<target name="build">
<error>Compilation error two!</error>
</target>
</project>
</target>
</project>
</target>
</project>
生成想要的正确结果:
Project Project1.csproj: 1 errors.
Errors:
Compilation error one!
Project Project2.csproj: 1 errors.
Errors:
Compilation error two!
答案 1 :(得分:1)
这是一个非常简单的解决方案:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="/">
<xsl:apply-templates select="//project[.//error]" />
</xsl:template>
<xsl:template match="project">
<xsl:variable name="errors" select=".//error[ancestor::project[1]/@name = current()/@name]" />
<xsl:if test="count($errors) != 0">
<xsl:value-of select="concat('Project "',@name,'": ',count($errors),' error(s) Error(s): ')" />
<xsl:apply-templates select="$errors" />
<xsl:text> </xsl:text>
</xsl:if>
</xsl:template>
<xsl:template match="error">
<xsl:value-of select="concat(.,' ')" />
</xsl:template>
</xsl:stylesheet>
此样式表基本上只调用文档树中任何位置的每个project
元素的模板。此模板使用xpath存储变量中所有错误的列表,该xpath选择所有error
个元素,这些元素是当前project
的后代并且具有当前project
元素,因为它是第一个{ {1}}祖先。然后,如果有,它只输出适当的标题文本并将模板应用于每个错误。
我在这里使用project
作为新的换行符,但如果你更喜欢windows换行符,可以使用
。
对此问题的轻微警告;你最终会在输出的底部添加一个空白行,因为它会在每个项目的底部添加一行以将其与下一个项目分开。
答案 2 :(得分:0)
我们习惯于复杂的要求,我认为当它看起来更简单时我们会感到困惑。
所以,用:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="project[../..]">
<xsl:value-of select="concat('
',
'Project "',@name,'": ',count(target/error),' error(s)
',
'Error(s):
')" />
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="error">
<xsl:value-of select="concat(.,'
')" />
</xsl:template>
</xsl:stylesheet>
你得到:
Project "Project1.csproj": 1 error(s)
Error(s):
Compilation error one!
Project "Project2.csproj": 1 error(s)
Error(s):
Compilation error two!