我正在尝试编写一个简单的算法来读取两个XML文件,它们具有完全相同的节点和结构,但不一定在子节点内的数据相同,而且顺序不一样.使用Microsoft的XML Diff.DLL,我如何创建一个简单的实现,用于创建第三个临时XML(两者之间的差异).
MSDN上的XML差异
http://msdn.microsoft.com/en-us/library/aa302294.aspx
http://msdn.microsoft.com/en-us/library/aa302295.aspx
<?xml version="1.0" encoding="utf-8" ?> <Stats Date="2011-01-01"> <Player Rank="1"> <Name>Sidney Crosby</Name> <Team>PIT</Team> <Pos>C</Pos> <GP>39</GP> <G>32</G> <A>33</A> <PlusMinus>20</PlusMinus> <PIM>29</PIM> </Player> </Stats> <?xml version="1.0" encoding="utf-8" ?> <Stats Date="2011-01-10"> <Player Rank="1"> <Name>Sidney Crosby</Name> <Team>PIT</Team> <Pos>C</Pos> <GP>42</GP> <G>35</G> <A>34</A> <PlusMinus>22</PlusMinus> <PIM>30</PIM> </Player> </Stats>
结果要求(两者之间的差异)
<?xml version="1.0" encoding="utf-8" ?> <Stats Date="2011-01-10"> <Player Rank="1"> <Name>Sidney Crosby</Name> <Team>PIT</Team> <Pos>C</Pos> <GP>3</GP> <G>3</G> <A>1</A> <PlusMinus>2</PlusMinus> <PIM>1</PIM> </Player> </Stats>
在这种情况下,我可能会使用XSLT将生成的XML“差异”文件转换为排序的HTML文件,但是我还没有.所有我想做的是在第三个XML文件中显示每个节点的每个数值的差异,从“GP”子节点开始.
C#代码我到目前为止
private void CompareXml(string file1,string file2) { XmlReader reader1 = XmlReader.Create(new StringReader(file1)); XmlReader reader2 = XmlReader.Create(new StringReader(file2)); string diffFile = StatsFile.XmlDiffFilename; StringBuilder differenceStringBuilder = new StringBuilder(); FileStream fs = new FileStream(diffFile,FileMode.Create); XmlWriter diffGramWriter = XmlWriter.Create(fs); XmlDiff xmldiff = new XmlDiff(XmlDiffOptions.IgnoreChildOrder | XmlDiffOptions.IgnoreNamespaces | XmlDiffOptions.IgnorePrefixes); bool bIdentical = xmldiff.Compare(file1,file2,false,diffGramWriter); diffGramWriter.Close(); // cleaning up after we are done with the xml diff file File.Delete(diffFile); }
}
这是我到目前为止,但结果是垃圾…注意,对于每个“玩家”节点,前三个孩子都没有比较…我该如何实现?
有两个即时解决方案:
解决方案1.
您可以先对两个文档应用一个简单的变换,这两个文档将删除不应该比较的元素.然后,将两个文档的结果与您当前的代码进行比较.这是转型:
<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="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="Name|Team|Pos"/> </xsl:stylesheet>
当将此转换应用于提供的XML文档时:
<Stats Date="2011-01-01"> <Player Rank="1"> <Name>Sidney Crosby</Name> <Team>PIT</Team> <Pos>C</Pos> <GP>39</GP> <G>32</G> <A>33</A> <PlusMinus>20</PlusMinus> <PIM>29</PIM> <PP>10</PP> <SH>1</SH> <GW>3</GW> <Shots>0</Shots> <ShotPctg>154</ShotPctg> <TOIPerGame>20.8</TOIPerGame> <ShiftsPerGame>21:54</ShiftsPerGame> <FOWinPctg>22.6</FOWinPctg> </Player> </Stats>
<Stats Date="2011-01-01"> <Player Rank="1"> <GP>39</GP> <G>32</G> <A>33</A> <PlusMinus>20</PlusMinus> <PIM>29</PIM> <PP>10</PP> <SH>1</SH> <GW>3</GW> <Shots>0</Shots> <ShotPctg>154</ShotPctg> <TOIPerGame>20.8</TOIPerGame> <ShiftsPerGame>21:54</ShiftsPerGame> <FOWinPctg>22.6</FOWinPctg> </Player> </Stats>
解决方案2.
这是一个完整的XSLT 1.0解决方案(仅为方便起见,第二个XML文档嵌入在转换代码中):
<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:variable name="vrtfDoc2"> <Stats Date="2011-01-01"> <Player Rank="2"> <Name>John Smith</Name> <Team>NY</Team> <Pos>D</Pos> <GP>38</GP> <G>32</G> <A>33</A> <PlusMinus>15</PlusMinus> <PIM>29</PIM> <PP>10</PP> <SH>1</SH> <GW>4</GW> <Shots>0</Shots> <ShotPctg>158</ShotPctg> <TOIPerGame>20.8</TOIPerGame> <ShiftsPerGame>21:54</ShiftsPerGame> <FOWinPctg>22.6</FOWinPctg> </Player> </Stats> </xsl:variable> <xsl:variable name="vDoc2" select= "document('')/*/xsl:variable[@name='vrtfDoc2']/*"/> <xsl:template match="node()|@*" name="identity"> <xsl:param name="pDoc2"/> <xsl:copy> <xsl:apply-templates select="node()|@*"> <xsl:with-param name="pDoc2" select="$pDoc2"/> </xsl:apply-templates> </xsl:copy> </xsl:template> <xsl:template match="/"> <xsl:apply-templates select="*"> <xsl:with-param name="pDoc2" select="$vDoc2"/> </xsl:apply-templates> ----------------------- <xsl:apply-templates select="$vDoc2"> <xsl:with-param name="pDoc2" select="/*"/> </xsl:apply-templates> </xsl:template> <xsl:template match="Player/*"> <xsl:param name="pDoc2"/> <xsl:if test= "not(. = $pDoc2/*/*[name()=name(current())])"> <xsl:call-template name="identity"/> </xsl:if> </xsl:template> <xsl:template match="Name|Team|Pos" priority="20"/> </xsl:stylesheet>
当该变换应用于与上述相同的第一个文档时,产生正确的diffgram:
<Stats Date="2011-01-01"> <Player Rank="1"> <GP>39</GP> <PlusMinus>20</PlusMinus> <GW>3</GW> <ShotPctg>154</ShotPctg> </Player> </Stats> ----------------------- <Stats xmlns:xsl="http://www.w3.org/1999/XSL/Transform" Date="2011-01-01"> <Player Rank="2"> <GP>38</GP> <PlusMinus>15</PlusMinus> <GW>4</GW> <ShotPctg>158</ShotPctg> </Player> </Stats>
这是如何工作的:
>转换应用于第一个文档,将第二个文档作为参数传递.>这产生一个XML文档,其唯一的叶元素节点是具有与第二个文档中相应的叶元素节点不同的值.>同样的处理按照上面的1.执行,但这次在第二个文档中,将第一个文档作为参数传递.>这产生第二个diffgram:一个XML文档,其唯一的叶元素节点是与第一个文档中相应的叶元素节点不同的值**