如何使用 XSLT 比较两个 XML 节点并获得比较结果? [英] How to compare two XML nodes and get compared result using XSLT?

查看:27
本文介绍了如何使用 XSLT 比较两个 XML 节点并获得比较结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

XML 文件作为输入和预期输出如下所示.可以参考链接...
XSLT 节点值对比

XML file as input and expected output shown below. Can refere link...
XSLT node value comparision

我正在寻找 xml 格式的输出,但如果有人制作以表格格式给出/显示结果的 xslt 文件,那就太好了.

I am looking for output in xml but it would be great if anyone make xslt file which gives/shows result in tabular format.

描述:XML 文件包含操作员站节点的集合,每个操作员站有多个网络,现在从每个操作员站的每个节点收集 IP 地址并显示其值并与每个操作员站进行比较,如下所示.如果每个操作员站网络 IP 都相等,那么它显示状态为相等,否则不相等.比较应该与 IPAddress 进行比较,并参考 Family 和 Name.就像将 OS01 的 IP 地址与其他具有相同系列(网络设置)和名称(网络 A)的操作系统进行比较.

Desc: XML file contains collection of operatorstation nodes, with each operator station has multiple networks, now collect IP address from each node of every operatorstation and show its value and compared status with every Operator station as shown below. If every operatorstation network IP is equal then it shows status as Equal otherwise Unequal. Comparison should be done with IPAddress with taking reference of Family and Name. Like compare IPAddress of OS01 with other OSs having same family (NetworkSettings) and Name (Network A).

XML 文件作为输入

![<?xml version="1.0" encoding="utf-8"?>
<OperatorStationCollection xmlns="http://www.w3.org" >
<OperatorStation xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Name>KM-OS001</Name>
    <Nodes>
      <DataNodeBase xsi:type="Adaptor">
        <Family>NetworkSettings</Family>
        <Name>Network A</Name>  
        <IPAddress>111.11.11.1</IPAddress>        
      </DataNodeBase>
      <DataNodeBase xsi:type="Adaptor">
        <Family>NetworkSettings</Family>
        <Name>Network B</Name>                
        <IPAddress>111.22.11.1</IPAddress>          
      </DataNodeBase>
      <DataNodeBase xsi:type="Adaptor">
        <Family>NetworkSettings</Family>
        <Name>Network C</Name>
        <IPAddress>111.33.11.1</IPAddress>
      </DataNodeBase>
      </Nodes>   
  </OperatorStation>      
<OperatorStation xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Name>KM-OS002</Name>
  <Nodes>
    <DataNodeBase xsi:type="Adaptor">
      <Family>NetworkSettings</Family>
      <Name>Network A</Name>
      <IPAddress>111.11.11.1</IPAddress>
    </DataNodeBase>
    <DataNodeBase xsi:type="Adaptor">
      <Family>NetworkSettings</Family>
      <Name>Network B</Name>
      <IPAddress>111.22.11.2</IPAddress>
    </DataNodeBase>
    <DataNodeBase xsi:type="Adaptor">
      <Family>NetworkSettings</Family>
      <Name>Network D</Name>
      <IPAddress>111.33.11.2</IPAddress>
    </DataNodeBase>
  </Nodes>
</OperatorStation>
  <OperatorStation xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Name>KM-OS003</Name>
    <Nodes>
      <DataNodeBase xsi:type="Adaptor">
        <Family>NetworkSettings</Family>
        <Name>Network A</Name>
        <IPAddress>111.11.11.1</IPAddress>
      </DataNodeBase>
      <DataNodeBase xsi:type="Adaptor">
        <Family>NetworkSettings</Family>
        <Name>Network B</Name>
        <IPAddress>111.22.11.3</IPAddress>
      </DataNodeBase>
      <DataNodeBase xsi:type="Adaptor">
        <Family>NetworkSettings</Family>
        <Name>Network E</Name>
        <IPAddress>111.33.11.3</IPAddress>
      </DataNodeBase>
    </Nodes>
  </OperatorStation>
</OperatorStationCollection>

预期输出.

使用 XSLT 的预期输出:这里没有添加表格的选项,所以请考虑将 <> 作为用于设计表格的分隔符,<> 不是结果的一部分,它只是添加到单独的列值中.请考虑下表中的结果.

Expected output using XSLT: Here no option to add table so please consider <> as separator which is used to design table, <> is not part of result it is just added to separate column values. Please consider below result as it is in table.

标题名称<>状态<>OS01<>OS02<>OS03

网络 A<>等于<>111.11.11.1<>111.11.11.1<>111.11.11.1

Network A<>Equal<>111.11.11.1<>111.11.11.1<>111.11.11.1

网络B<>不平等<>111.22.11.1<>111.22.11.2<>111.22.11.2

Network B<>Unequal<>111.22.11.1<>111.22.11.2<>111.22.11.2

网络C<>不等<>111.33.11.1<>不存在<>不存在

Network C<>Unequal<>111.33.11.1<>Not Exist<>Not Exist

网络D<>不平等<>不存在<>111.33.11.2<>不存在

Network D<>Unequal<>Not Exist<>111.33.11.2<>Not Exist

网络E<>不平等<>不存在<>不存在<>111.33.11.3

Network E<>Unequal<>Not Exist<>Not Exist<>111.33.11.3

下面的 OR 代码以表格格式显示预期结果.保存以下代码 fileName.html.

OR Code below shows expected result in tabular format. Save below code fileName.html.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>    
</head>
<body>

   <table>
            <tr>
            <td>Name</td><td>Status</td><td>OS01</td><td>OS02</td><td>OS03</td>
            </tr>
            <tr>
            <td>Network A</td><td>Equal</td><td>111.11.11.1</td><td>111.11.11.1</td><td>111.11.11.1</td>
            </tr>
            <tr>
            <td>Network B</td><td>Unequal</td><td>111.22.11.1</td><td>111.22.11.2</td><td>111.22.11.2</td>
            </tr>
            <tr>
            <td>Network C</td><td>Unequal</td><td>111.33.11.1</td><td>Not Exist</td><td>Not Exist</td>
            </tr>
            <tr>
            <td>Network D</td><td>Unequal</td><td>Not Exist</td><td>111.33.11.2</td><td>Not Exist</td>
            </tr>
            <tr>
            <td>Network E</td><td>Unequal</td><td>Not Exist</td><td>Not Exist</td><td>111.33.11.3</td>
            </tr>           
            </table>

</body>
</html>

推荐答案

所以您希望每个操作员站名称占一列,每个不同网络名称占一行.在 XSLT 2.0 中,这可以使用 for-each-group

So you want one column per operator station name, and one row per distinct network name. In XSLT 2.0 this can be done nicely using for-each-group

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
    xmlns:w3="http://www.w3.org" exclude-result-prefixes="w3">
  <xsl:template match="/">
    <xsl:variable name="allStations"
                  select="/w3:OperatorStationCollection/w3:OperatorStation" />
    <table>
      <!-- Header row - two fixed columns plus one per station name -->
      <tr>
        <td>Name</td><td>Status</td>
        <xsl:for-each select="$allStations">
          <td><xsl:value-of select="w3:Name" /></td>
        </xsl:for-each>
      </tr>
      <!-- main rows - one per "group" of DataNodeBase elements which share the
           same Name -->
      <xsl:for-each-group
          select="$allStations/w3:Nodes/w3:DataNodeBase"
          group-by="w3:Name">
        <!-- calculate the column values - the IPAddress if this network (i.e. the
             current-group) has an entry for this station, and "None" if not -->
        <xsl:variable name="addresses"
            select="for $s in ($allStations)
                    return (current-group()[../.. is $s]/w3:IPAddress, 'None')[1]" />
        <tr>
          <td><xsl:value-of select="current-grouping-key()" /></td>
          <td>
            <!-- equal if all the $addresses are the same, unequal otherwise -->
            <xsl:value-of select="if (count(distinct-values($addresses)) = 1)
                                  then 'Equal' else 'Unequal'" />
          </td>
          <xsl:for-each select="$addresses">
            <td><xsl:value-of select="."/></td>
          </xsl:for-each>
        </tr>
      </xsl:for-each-group>
    </table>
  </xsl:template>
</xsl:stylesheet>

如果您仅限于 1.0,那么逻辑是相同的,但样式表要冗长得多 - 您必须使用Muenchian grouping"方法来替换 for-each-group,并且相等/不等计算有点混乱,因为您没有 distinct-values 函数或定义任意值序列的能力($addresses 变量在 2.0 版本中):

If you're limited to 1.0 then the logic is the same but the stylesheet is much more verbose - you'll have to use the "Muenchian grouping" method to replace for-each-group, and the equal/unequal calculation is a bit messier because you don't have the distinct-values function or the capability to define arbitrary sequences of values (the $addresses variable in the 2.0 version):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
     xmlns:w3="http://www.w3.org" exclude-result-prefixes="w3">

  <!-- grouping key to pull out all the DataNodeBase elements with a
       particular name -->
  <xsl:key name="dnbByName" match="w3:DataNodeBase" use="w3:Name" />

  <xsl:template match="/">
    <xsl:variable name="allStations"
                  select="/w3:OperatorStationCollection/w3:OperatorStation" />
    <table>
      <tr>
        <td>Name</td><td>Status</td>
        <xsl:for-each select="$allStations">
          <td><xsl:value-of select="w3:Name" /></td>
        </xsl:for-each>
      </tr>
      <!-- Muenchian grouping - for-each over a set consisting of just one
           DataNodeBase per network name "group" -->
      <xsl:for-each select="$allStations/w3:Nodes/w3:DataNodeBase[
            generate-id() = generate-id(key('dnbByName', w3:Name)[1])]">
        <xsl:variable name="current-group" select="key('dnbByName', w3:Name)" />
        <xsl:variable name="current-grouping-key" select="w3:Name" />
        <tr>
          <td><xsl:value-of select="$current-grouping-key" /></td>
          <td>
            <xsl:choose>
              <!-- "Equal" if all stations have a value for this network name,
                   and all these values are the same (it is not the case that
                   any of the values is different from that of the first
                   station) -->
              <xsl:when test="count($current-group) = count($allStations)
                  and not($current-group/w3:IPAddress
                          != $current-group[1]/w3:IPAddress)">
                <xsl:text>Equal</xsl:text>
              </xsl:when>
              <xsl:otherwise>
                <xsl:text>Unequal</xsl:text>
              </xsl:otherwise>
            </xsl:choose>
          </td>
          <!-- remaining columns, one per station -->
          <xsl:for-each select="$allStations">
            <td>
              <!-- check whether this station has an address for this network -->
              <xsl:variable name="address" select="w3:Nodes/w3:DataNodeBase[
                      w3:Name = $current-grouping-key]/w3:IPAddress" />
              <xsl:choose>
                <xsl:when test="$address">
                  <xsl:value-of select="$address" />
                </xsl:when>
                <xsl:otherwise>None</xsl:otherwise>
              </xsl:choose>
            </td>
          </xsl:for-each>
        </tr>
      </xsl:for-each>
    </table>
  </xsl:template>
</xsl:stylesheet>

这篇关于如何使用 XSLT 比较两个 XML 节点并获得比较结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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