首页 > 解决方案 > 使用 xslt 合并两个 xml

问题描述

我想使用 xslt 将两个 xml 文件合并为一个。

文件 1:

<W>
<S event="idle" guid="328775aa-87fd-4f9c-b474-7b5d9d5a63b8" />
<S event="moving" guid="2db271fe-56c5-9ac8-9050-e2db2a653165" />
<S event="moving" guid="f90571fe-57f2-4204-9dd0-e2db2a634feb" />
<S event="moving" guid="4759ac82-d4f2-4fb3-9e60-36c531650cce" />
<S event="moving" guid="67450f05-eb20-4306-97aa-0357b9e83589" />
<S event="moving" guid="32f91c48-4865-48aa-a1c1-2a80b1b73cf6" />
<S event="idle" guid="33a06ae1-69a2-41f3-b295-663579b2477e" />
</W>

文件 2:

<EpiVPED>
 <vpeds guid="2db271fe-56c5-9ac8-9050-e2db2a653165" attachto="current">
    <dp>
      <name>Current</name>
    </dp>
  </vpeds>
  <vpeds guid="f90571fe-57f2-4204-9dd0-e2db2a634feb" attachto="start">
    <dp>
      <name>Start</name>
    </dp>
  </vpeds>

  <vpeds guid="4759ac82-d4f2-4fb3-9e60-36c531650cce" attachto="end">
    <dp>
      <name>End</name>
    </dp>
  </vpeds>
</EpiVPED>

合并:

<W>
<S event="idle" guid="328775aa-87fd-4f9c-b474-7b5d9d5a63b8">
 <DataHandler>
    <vpeds guid="f90571fe-57f2-4204-9dd0-e2db2a634feb" attachto="start">
    <dp>
      <name>Start</name>
    </dp>
  </vpeds>
  </DataHandler>
</S>

<S event="moving"  guid="2db271fe-56c5-9ac8-9050-e2db2a653165" >
 <DataHandler>
    <vpeds guid="2db271fe-56c5-9ac8-9050-e2db2a653165" attachto="current">
    <dp>
      <name>Current</name>
    </dp>
  </vpeds>
  </DataHandler>
</S>
<S event="moving"  guid="f90571fe-57f2-4204-9dd0-e2db2a634feb" >
    <DataHandler />
</S>
<S event="moving" guid="4759ac82-d4f2-4fb3-9e60-36c531650cce" >
    <DataHandler />
</S>
<S event="moving" guid="67450f05-eb20-4306-97aa-0357b9e83589" >
    <DataHandler />
</S>
<S event="moving" guid="32f91c48-4865-48aa-a1c1-2a80b1b73cf6">
 <DataHandler>
    <vpeds guid="4759ac82-d4f2-4fb3-9e60-36c531650cce" attachto="end">
    <dp>
      <name>End</name>
    </dp>
  </vpeds>
  </DataHandler>
</S> 

<S event="idle" guid="33a06ae1-69a2-41f3-b295-663579b2477e">
    <DataHandler />
</S>
</W>

如果满足以下条件,这里我需要将 vpeds 节点(文件 2)复制到 S 节点(文件 1)中

1) 如果 vpeds 和 S 的 guid 都匹配 AND attachto == current,则将 vpeds 节点复制到 S 节点

2) 如果 vpeds 和 S 的 guid 都匹配 AND attachto == start,则将 vpeds 节点复制到具有 event == idle 的前面 S

3) 如果 vpeds 和 S 的 guid 都匹配 AND attachto == end,则将 vpeds 节点复制到具有 event == idle 的下一个 S 节点的直接 S 节点

我尝试使用以下代码

在 C# 代码中,我将 file1 作为主文件传递,将 file2 作为参数传递。

XmlDocument OutputGPSDoc= new XmlDocument();
XsltSettings xslsettings = new XsltSettings
{
    EnableScript = true,
    EnableDocumentFunction=true
};
XsltArgumentList _xslArg = new XsltArgumentList();
_xslArg.AddParam("vpedFilePath", "", @"D:\file2.xml");
XmlResolver secureResolver = new XmlSecureResolver(new XmlUrlResolver(), xslPath);
XslCompiledTransform oXslt = new XslCompiledTransform(true);
oXslt.Load(xslPath, xslsettings, secureResolver);

XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Parse;
settings.IgnoreWhitespace = true;
using (XmlReader reader = XmlReader.Create(@"D:\file1.xml", settings))
{
     StringBuilder builder = new StringBuilder();
     using (StringWriter stringWriter = new StringWriter(builder))
     {
         using (XmlWriter writer = XmlWriter.Create(stringWriter))
         {
              oXslt.Transform(reader, _xslArg, writer);
         }
      }
  OutputGPSDoc.LoadXml(builder.ToString());
  builder = null;
}
OutputGPSDoc.Save(@"D:\merged.xml");

下面的xslt代码是为了改造

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                xmlns:utils="urn:myExtension">
    <xsl:output method="xml" encoding="utf-16" />
    <xsl:param name="vpedFilePath"/>
    <xsl:param name="updates" select="document($vpedFilePath)" />
    <xsl:variable name="updateItems" select="$updates/EpiVPED/./*" />

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:strip-space elements="S" />
    <xsl:template match="/">
        <xsl:apply-templates select="/W" />
    </xsl:template>
    <xsl:template match="/W">
        <xsl:element name="W">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>
    <xsl:template match="/W/S">
        <xsl:element name="S">
            <xsl:attribute name="event">
                <xsl:value-of select= "@event" />
            </xsl:attribute>
            <xsl:variable name="GUID">
                <xsl:value-of select= "@guid" />
            </xsl:variable>
            <xsl:attribute name="guid">
                <xsl:value-of select= "$GUID" />
            </xsl:attribute>
            <xsl:element name="DataHandler">
                <xsl:choose>
                    <xsl:when test="@event='idle'">
                        <!-- need to copy if satisfies the condition mentioned -->
                    </xsl:when>
                    <xsl:when test="following-sibling::S[1]/@event='idle'">
                        <!-- need to copy if satisfies the condition mentioned -->
                    </xsl:when>
                    <xsl:when test="$updateItems[@guid=$GUID and @attachto='2']">
                        <xsl:copy-of select="$updateItems[@guid=$Guid]"/>        
                    </xsl:when>      
                </xsl:choose>      
            </xsl:element>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

我不知道该怎么做。因为我是 xslt 的新手。有人有什么想法吗?我使用 XSLT 1.0 版。

标签: xmlxsltc#-4.0xml-parsingxslt-1.0

解决方案


试试这个

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                xmlns:utils="urn:myExtension">
    <xsl:output method="xml" encoding="utf-16" />
    <xsl:param name="vpedFilePath"/>
    <xsl:param name="updates" select="document($vpedFilePath)" />
    <xsl:variable name="updateItems" select="$updates/EpiVPED/./*" />

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:strip-space elements="S" />
    <xsl:variable name="stepCount" select="count(/W/S)"/>
    <xsl:template match="/">
        <xsl:apply-templates select="/W" />
    </xsl:template>
    <xsl:template match="/W">
        <xsl:element name="W">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>
    <xsl:template match="/W/S">
        <xsl:variable name="curIndex">
            <xsl:number/>    
        </xsl:variable>

        <xsl:variable name="CurrentStepEventName">      
            <xsl:value-of select="@event" />
        </xsl:variable>      

        <xsl:variable name="FollowingStepStepEventName">
            <xsl:value-of select="following-sibling::S[1]/@event" />
        </xsl:variable> 

        <xsl:element name="S">
      <xsl:variable name="GUID">
              <xsl:value-of select= "@guid" />
          </xsl:variable>
            <xsl:attribute name="guid">
                <xsl:value-of select= "@guid" />
            </xsl:attribute>

      <xsl:attribute name="event">
              <xsl:value-of select= "@event" />
          </xsl:attribute>

            <xsl:element name="DataHandler">        
                <xsl:choose>
                    <xsl:when test="$CurrentStepEventName='idle'">
                        <xsl:variable name="vpedGUID"  >
                            <xsl:call-template name="GetEntryStepVPEDGUID">
                                <xsl:with-param name="idx" select="1" />
                                <xsl:with-param name="curIndex" select="$curIndex" />
                                <xsl:with-param name="isCurStepProcessed" select="0" />
                            </xsl:call-template>          
                        </xsl:variable>
                        <xsl:if test="not($vpedGUID = '')">
                            <xsl:copy-of select="$updateItems[@guid = $vpedGUID]"/>
                        </xsl:if>          
                    </xsl:when>
                    <xsl:when test="$FollowingStepStepEventName='idle'">
                        <xsl:variable name="vpedGUID"  >
                            <xsl:call-template name="GetExitStepVPEDGUID">
                                <xsl:with-param name="idx" select="1" />
                                <xsl:with-param name="curIndex" select="$curIndex" />
                            </xsl:call-template>          
                        </xsl:variable>
                        <xsl:if test="not($vpedGUID = '')">
                            <xsl:copy-of select="$updateItems[@guid = $vpedGUID]"/>
                        </xsl:if>
                    </xsl:when>
                    <xsl:when test="$updateItems[@guid = $GUID and @attachto='current']">
                        <xsl:copy-of select="$updateItems[@guid = $GUID]"/>        
                    </xsl:when>      
                </xsl:choose>      
            </xsl:element>

        </xsl:element>
    </xsl:template>


    <xsl:template name="GetEntryStepVPEDGUID">
        <xsl:param name="idx" />
        <xsl:param name="curIndex" />
        <xsl:param name="isCurStepProcessed"/>
        <xsl:variable name="followingStepGUID">
            <xsl:choose>
                <xsl:when test="not(($idx + $curIndex) &gt;= $stepCount)">
                    <xsl:value-of select="following-sibling::S[$idx]/@guid"/>       
                </xsl:when>         
                <xsl:otherwise/>      
            </xsl:choose>      
        </xsl:variable>

        <xsl:variable name="followingStepEvent">
            <xsl:choose>
                <xsl:when test="not(($idx + $curIndex) &gt;= $stepCount)">
                    <xsl:value-of select="following-sibling::S[$idx]/@event"/>   
                </xsl:when>         
                <xsl:otherwise/>      
            </xsl:choose>       
        </xsl:variable>

        <xsl:variable name="currentStepGUID">
            <xsl:value-of select="@guid"/>
        </xsl:variable>

        <xsl:variable name="currentStepEvent">
            <xsl:value-of select="@event"/>
        </xsl:variable>

        <xsl:choose>
            <xsl:when test="$currentStepEvent = 'idle' and $updateItems[@guid = $currentStepGUID and (@attachto='start' or @attachto='current')] and $isCurStepProcessed = '0'">
                <xsl:value-of select="$currentStepGUID"/>      
                <xsl:call-template name="GetEntryStepVPEDGUID">
                    <xsl:with-param name="idx" select="($idx)" />
                    <xsl:with-param name="curIndex" select="$curIndex" />
                    <xsl:with-param name="isCurStepProcessed" select="($isCurStepProcessed + 1)" />
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="$followingStepEvent = 'idle' or ($idx + $curIndex) &gt;= $stepCount">
            </xsl:when>
            <xsl:when test="$updateItems[@guid = $followingStepGUID and @attachto='start']">
                <xsl:value-of select="$followingStepGUID"/>
                <xsl:call-template name="GetEntryStepVPEDGUID">
                    <xsl:with-param name="idx" select="($idx + 1)" />
                    <xsl:with-param name="curIndex" select="$curIndex" />
                    <xsl:with-param name="isCurStepProcessed" select="($isCurStepProcessed + 1)" />
                </xsl:call-template>  
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="GetEntryStepVPEDGUID">
                    <xsl:with-param name="idx" select="($idx + 1)" />
                    <xsl:with-param name="curIndex" select="$curIndex" />
                    <xsl:with-param name="isCurStepProcessed" select="($isCurStepProcessed + 1)" />
                </xsl:call-template>      
            </xsl:otherwise>
        </xsl:choose>

    </xsl:template>

    <xsl:template name="GetExitStepVPEDGUID">
        <xsl:param name="idx" />
        <xsl:param name="curIndex" />
        <xsl:variable name="precedingStepGUID">
            <xsl:choose>
                <xsl:when test="not(($curIndex - $idx) &lt;= 0)">
                    <xsl:value-of select="preceding-sibling::S[$idx]/@guid"/>
                </xsl:when>
                <xsl:otherwise/>
            </xsl:choose>
        </xsl:variable>

        <xsl:variable name="precedingStepEvent">
            <xsl:choose>
                <xsl:when test="not(($curIndex - $idx) &lt;= 0)">
                    <xsl:value-of select="preceding-sibling::S[$idx]/@event"/>    
                </xsl:when>
                <xsl:otherwise/>
            </xsl:choose>
        </xsl:variable>

        <xsl:choose>      
            <xsl:when test="$precedingStepEvent = 'idle'">
                <xsl:if test="$updateItems[@guid = $precedingStepGUID and @attachto='end']">
                    <xsl:value-of select="$precedingStepGUID"/>
                </xsl:if>      
            </xsl:when>
            <xsl:when test="($curIndex - $idx) &lt;= 0"/>
            <xsl:when test="$updateItems[@guid = $precedingStepGUID and @attachto='end']">
                <xsl:value-of select="$precedingStepGUID"/>
                <xsl:call-template name="GetExitStepVPEDGUID">
                    <xsl:with-param name="idx" select="($idx + 1)" />
                    <xsl:with-param name="curIndex" select="$curIndex" />
                </xsl:call-template> 
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="GetExitStepVPEDGUID">
                    <xsl:with-param name="idx" select="($idx + 1)" />
                    <xsl:with-param name="curIndex" select="$curIndex" />
                </xsl:call-template>      
            </xsl:otherwise>
        </xsl:choose>

    </xsl:template>

</xsl:stylesheet>

推荐阅读