2013年7月18日木曜日

【XSLT】 XSLTで2つのXMLをマージしてみた

XSLTで2つのデータのマージをしたいとおもって色々探しまわったけど、見つからなかったので自作してみた。

こういう用途ってないのかな??

saxonでしか確認してない。そもそも2.0でしか動かない!

call-templateはネストすると処理が重くなるため、apply-templatesで処理してます。
nodes1に放り込んだxmlに対して、nodes2を上書きしていきます。

要素の順番はnodes1側に引きづられ、nodes2側にしかない要素(追加したい要素)は兄弟要素として末尾に追加されます。

マージしたxmlに対してxpathで取得する際には問題が起きにくいですが、ノード番号等で取得するような場合は注意が必要です。

さて、次はドキュメント比較用のXSLTを作らねば・・・

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="@*|node()" mode="marge">
 <xsl:param name="nodes1" as="item()*" />
 <xsl:param name="nodes2" as="item()*" />
 <xsl:variable name="node1" select="$nodes1[1]" as="item()*" />
 <xsl:variable name="node2" as="item()*">
  <xsl:choose>
   <xsl:when test="$node1">
    <xsl:copy-of select="$nodes2[name() = $node1/name()][position() = 1]" />
   </xsl:when>
   <xsl:otherwise>
    <xsl:copy-of select="$nodes2[1]" />
   </xsl:otherwise>
  </xsl:choose>
 </xsl:variable>
 <xsl:variable name="nodes1After" as="item()*">
  <xsl:copy-of select="$nodes1[position() != 1]" />
 </xsl:variable>
 <xsl:variable name="nodes2After" as="item()*">
  <xsl:choose>
   <xsl:when test="$node1">
    <xsl:copy-of select="$nodes2[not(name() = $node1/name() and position() = 1)]" />
   </xsl:when>
   <xsl:otherwise>
    <xsl:copy-of select="$nodes2[position() != 1]"></xsl:copy-of>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:variable>
 
 <xsl:choose>
  <xsl:when test="not($node1)">
   <xsl:copy-of select="$node2" />
  </xsl:when>
  <xsl:when test="$node1">
   <xsl:element name="{$node1/name()}">
    <xsl:apply-templates select="$node1/@*" />
    <xsl:apply-templates select="$node2/@*" />
    <!-- 子要素 -->
    <xsl:apply-templates select="self::node()" mode="marge">
     <xsl:with-param name="nodes1" select="$node1/node()" />
     <xsl:with-param name="nodes2" select="$node2/node()" />
    </xsl:apply-templates>
   </xsl:element>
   <xsl:apply-templates select="self::node()" mode="marge">
    <xsl:with-param name="nodes1" select="$nodes1After" />
    <xsl:with-param name="nodes2" select="$nodes2After" />
   </xsl:apply-templates>
  </xsl:when>
  <xsl:otherwise />
 </xsl:choose>
</xsl:template>
</xsl:stylesheet>

0 件のコメント:

コメントを投稿