开发者

XSLT to find all nodes in one list that aren't in another

I have xml that essentialy looks like the following (my actual .xml is a build log containing a union of VStudio and DotCover build output):

<buildlog>
  <Projects>
    <project name="project1.csproj" />
    <project name="project2.csproj" />
    <project name="project3.csproj" />
    <project name="project4.csproj" />
  </Projects>
  <Root>
    <Assembly Name="project1" />
    <Assembly Name="project2" />
  </Root>
</buildlog>

I need a list of all project nodes that don't have a corresponding @name match on @Name in the set of Root/Assembly nodes. My goal here is to generate a report showing all projects that don't have code coverage.

Here's the beginnings of my xsl stylesheet:

    <xsl:template match="/">
        <xsl:apply-templates select="buildlog/Projects" />
      </xsl:template>

      <xsl:template match="Projects">

        <table>
          <tr>
             <td>Assemblies Not Covered:</td>
             <td><xsl:value-of select="count(./project)"/></td>
          </tr>
        </table>

        <xsl:apply-templates select="project" />

      </xsl:template>

      <xsl:template match="project">

        <table>
          <tr>
            <td style="padding-left:30px">
              <xsl:value-of select="substring-before(./@name,'.csproj')"/>
            </td>
          </tr>
        </table>

      </xsl:template>

Thanks in advance.

INTERIM SOLUTION

For the time being I solved my issue using ms:script extensions. Below is the code. Not the most elegant give it is a Windows only solution, but it solved the problem. I'll the things other folks recommended and answer back in the comment sections.

Here's my solution:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns="http://www.w3.org/TR/xhtml1/strict"
                xmlns:ms="urn:schemas-microsoft-com:xslt"
                xmlns:dsi="urn:dsi-scripts">
  <xsl:output method="html"/>

  <xsl:template match="/">

    <xsl:apply-templates select="buildlog/Projects" />


  </xsl:template>

  <xsl:template match="Projects">
    <table>
      <tr>
        <td>As开发者_如何学运维semblies Not Covered:</td>
      </tr>
    </table>

    <xsl:apply-templates select="project" />

  </xsl:template>

  <xsl:template match="project">
    <xsl:variable name="assemblies" select="//Assembly" />

    <xsl:if test="not(dsi:HasCoverage($assemblies/@Name, ./@name))">
      <table>
        <tr>
          <td style="padding-left:30px">
            <xsl:value-of select="substring-before(./@name,'.csproj')"/>
          </td>
        </tr>
      </table>
    </xsl:if>

  </xsl:template>

  <xsl:template match="Assembly">
    <table>
      <tr>
        <td style="padding-left:30px">
          <xsl:value-of select="@Name"/>
        </td>
      </tr>
    </table>
  </xsl:template>

  <ms:script language="C#" implements-prefix="dsi">
    <![CDATA[
      public bool HasCoverage(XPathNodeIterator assemblies, string project)
      {
        bool result = false;
        while(assemblies.MoveNext())
        {
          if(assemblies.Current.Value == project.Replace(".csproj",string.Empty))
          {
            result = true;
            break;
          }
        }
        return result;
      }

    ]]>
  </ms:script>

</xsl:stylesheet>


I need a list of all project nodes that don't have a corresponding @name match on @Name in the set of Root/Assembly nodes

Use:

/*/Projects/project
     [not(substring-before(@name, '.csproj') = /*/Root/Assembly/@Name)]

This XPath expression selects exactly the wanted project elements.

Complete solution:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match=
    "Projects/project
        [not(substring-before(@name, '.csproj')
        =
         /*/Root/Assembly/@Name)] ">
  <xsl:value-of select="concat(@name,'&#xA;')"/>
 </xsl:template>

</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<buildlog>
    <Projects>
        <project name="project1.csproj" />
        <project name="project2.csproj" />
        <project name="project3.csproj" />
        <project name="project4.csproj" />
    </Projects>
    <Root>
        <Assembly Name="project1" />
        <Assembly Name="project2" />
    </Root>
</buildlog>

the wanted result is produced:

project3.csproj
project4.csproj

In case more efficiency becomes really necessary, one should use keys.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜