使用 XSLT 转换后保留 XML 文件的层次结构(顺序)

keep the hierarchy (order) of an XML file after transformation with XSLT

我正在尝试将一个 XML 文件转换为另一个文件,但我在保持输入文件的相同层次结构方面遇到了问题。

这是输入:

<XMI xmi.version='1.2' xmlns:UML="org.omg.xmi.namespace.UML">
  <XMI.content>
    <UML:Model xmi.id='eee_1045467100313_135436_1' name='Data'>
      <UML:Namespace.ownedElement>
        <UML:Package xmi.id='_9_0_bc102e5_1427365805826_580042_23' name='migration_kbir'>
          <UML:Namespace.ownedElement>
            <UML:Package xmi.id='_9_0_bc102e5_1427365805826_580042_22' name='migration_s8ir'>
              <UML:Package xmi.id='_9_0_bc102e5_1427365805826_580042_22' name='migration_s8ir2'>
                <UML:Class xmi.id='_9_0_bc102e5_1427367042666_255023_151' name='Employee'>
                  <UML:Classifier.feature>
                    <UML:Attribute xmi.id='_9_0_bc102e5_1427367052819_893122_168' name='cin'>
                    </UML:Attribute>
                  </UML:Classifier.feature>
                </UML:Class>
                <UML:Class xmi.id='_9_0_64701d4_1429716452808_363115_43' name='AssoEmpl'>
                </UML:Class>
                <UML:Generalization xmi.id='_9_0_64701d4_1429719509242_100032_198' child='_9_0_64701d4_1429719498101_197360_182' parent='_9_0_64701d4_1429716437842_892182_26' />
              </UML:Package>
            </UML:Package>
          </UML:Namespace.ownedElement>
        </UML:Package>
      </UML:Namespace.ownedElement>
    </UML:Model>
  </XMI.content>
</XMI>

这是我得到的输出:

<xmi:XMI xmlns:xmi="http://www.omg.org/XMI" xmlns:UML="org.omg.xmi.namespace.UML"
  xmi.version="2.1" timestamp="">
  <packagedElement xmi:type="uml:package"
    xmi.id="_9_0_bc102e5_1427365805826_580042_23" name="migration_kbir" />
  <packagedElement xmi:type="uml:package"
    xmi.id="_9_0_bc102e5_1427365805826_580042_22" name="migration_s8ir" />
  <packagedElement xmi:type="uml:package"
    xmi.id="_9_0_bc102e5_1427365805826_580042_22" name="migration_s8ir2">
    <packagedElement xmi:type="uml:Class"
      xmi.id="_9_0_bc102e5_1427367042666_255023_151" name="Employee">
      <generalization xmi.id="_9_0_bc102e5_1427367042666_255023_151">
        <general xmi:type="uml:Class" />
      </generalization>
      <ownedAttribute xmi.id="_9_0_bc102e5_1427367052819_893122_168"
        name="cin" />
    </packagedElement>
    <packagedElement xmi:type="uml:Class"
      xmi.id="_9_0_64701d4_1429716452808_363115_43" name="AssoEmpl">
      <generalization xmi.id="_9_0_64701d4_1429716452808_363115_43">
        <general xmi:type="uml:Class" />
      </generalization>
    </packagedElement>
  </packagedElement>
</xmi:XMI>

但我期待这样的结果:

<xmi:XMI xmlns:xmi="http://www.omg.org/XMI" xmlns:UML="org.omg.xmi.namespace.UML" xmi.version="2.1" timestamp="">
  <packagedElement xmi:type="uml:package" xmi.id="_9_0_bc102e5_1427365805826_580042_23" name="migration_kbir">
      <packagedElement xmi:type="uml:package"xmi.id="_9_0_bc102e5_1427365805826_580042_22" name="migration_s8ir">
          <packagedElement xmi:type="uml:package"xmi.id="_9_0_bc102e5_1427365805826_580042_22" name="migration_s8ir2">
              <packagedElement xmi:type="uml:Class"xmi.id="_9_0_bc102e5_1427367042666_255023_151" name="Employee">
                  <generalization xmi.id="_9_0_bc102e5_1427367042666_255023_151">
                    <general xmi:type="uml:Class" />
                  </generalization>
                  <ownedAttribute xmi.id="_9_0_bc102e5_1427367052819_893122_168"name="cin" />
              </packagedElement>
              <packagedElement xmi:type="uml:Class" xmi.id="_9_0_64701d4_1429716452808_363115_43" name="AssoEmpl">
                  <generalization xmi.id="_9_0_64701d4_1429716452808_363115_43">
                    <general xmi:type="uml:Class" />
                  </generalization>
              </packagedElement>
          </packagedElement>
      </packagedElement>
  </packagedElement>
</xmi:XMI>

最后是我的转换文件:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:UML="org.omg.xmi.namespace.UML"
  xmlns:xmi="http://www.omg.org/XMI"
>
  <xsl:output indent="yes" method="xml" />

  <xsl:template match="/">
    <xmi:XMI>
      <xsl:attribute name="xmi.version">2.1</xsl:attribute>
      <xsl:attribute name="timestamp">
        <xsl:value-of select="@timestamp" />
      </xsl:attribute>
      <xsl:for-each select="//UML:Package">
        <packagedElement>
          <xsl:attribute name="xmi:type">uml:package</xsl:attribute>
          <xsl:attribute name="xmi.id">
            <xsl:value-of select="@xmi.id" />
          </xsl:attribute>
          <xsl:attribute name="name">
            <xsl:value-of select="@name" />
          </xsl:attribute>
          <xsl:choose>
            <xsl:when test="UML:Class">
              <xsl:for-each select="UML:Class">
                <packagedElement>
                  <xsl:attribute name="xmi:type">uml:Class</xsl:attribute>
                  <xsl:attribute name="xmi.id">
                    <xsl:value-of select="@xmi.id" />
                  </xsl:attribute>
                  <xsl:attribute name="name">
                    <xsl:value-of select="@name" />
                  </xsl:attribute>
                  <generalization>
                    <xsl:attribute name="xmi.id">
                      <xsl:value-of select="@xmi.id" />
                    </xsl:attribute>
                    <general xmi:type="uml:Class" />
                  </generalization>
                  <xsl:for-each select="UML:Classifier.feature/UML:Attribute">
                    <ownedAttribute>
                      <xsl:attribute name="xmi.id">
                        <xsl:value-of select="@xmi.id" />
                      </xsl:attribute>
                      <xsl:attribute name="name">
                        <xsl:value-of select="@name" />
                      </xsl:attribute>
                    </ownedAttribute>
                  </xsl:for-each>
                </packagedElement>
              </xsl:for-each>
            </xsl:when>
          </xsl:choose>
        </packagedElement>
      </xsl:for-each>
    </xmi:XMI>
  </xsl:template>
</xsl:stylesheet>

我应该在转换代码中做什么来保持输入文件的层次结构?

当你这样做时:

<xsl:for-each select="//UML:Package">

您正在选择文档中的 所有 UML:Package 元素,任意位置,并将它们视为兄弟姐妹。由于这不是您想要的,因此您需要更改方法并改为递归应用模板。

我已经对您现有的样式表进行了快速调整,看看是否适合您:

<xsl:stylesheet xmlns:UML="org.omg.xmi.namespace.UML" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" method="xml"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/">
    <xmi:XMI xmi.version="2.1">
        <xsl:attribute name="xmi.version">2.1</xsl:attribute>
        <xsl:attribute name="timestamp">
            <xsl:value-of select="@timestamp"/>
        </xsl:attribute>
        <xsl:apply-templates/>  
    </xmi:XMI>  
</xsl:template> 

<xsl:template match="UML:Package">      
    <packagedElement>
      <xsl:attribute name="xmi:type">uml:package</xsl:attribute>
      <xsl:attribute name="xmi.id">
        <xsl:value-of select="@xmi.id"/>
      </xsl:attribute>
      <xsl:attribute name="name">
        <xsl:value-of select="@name"/>
      </xsl:attribute>
      <xsl:choose>
        <xsl:when test="UML:Class">
          <xsl:for-each select="UML:Class">
            <packagedElement>
              <xsl:attribute name="xmi:type">uml:Class</xsl:attribute>
              <xsl:attribute name="xmi.id">
                <xsl:value-of select="@xmi.id"/>
              </xsl:attribute>
              <xsl:attribute name="name">
                <xsl:value-of select="@name"/>
              </xsl:attribute>
              <generalization>
                <xsl:attribute name="xmi.id">
                  <xsl:value-of select="@xmi.id"/>
                </xsl:attribute>
                <general xmi:type="uml:Class"/>
              </generalization>
              <xsl:for-each select="UML:Classifier.feature/UML:Attribute">
                <ownedAttribute>
                  <xsl:attribute name="xmi.id">
                    <xsl:value-of select="@xmi.id"/>
                  </xsl:attribute>
                  <xsl:attribute name="name">
                    <xsl:value-of select="@name"/>
                  </xsl:attribute>
                </ownedAttribute>
              </xsl:for-each>
            </packagedElement>
          </xsl:for-each>
        </xsl:when>
      </xsl:choose>
      <xsl:apply-templates/>  
    </packagedElement>
</xsl:template>

</xsl:stylesheet>

注意

  1. 您应该了解如何将属性直接添加到文字结果元素,以及如何使用属性值模板。这可以大大简化您的样式表。

  2. 这个:

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

    在根节点的上下文中没有任何意义 属性.

如果您想要 推送式 类型的样式表:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:UML="org.omg.xmi.namespace.UML"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xmi="http://www.omg.org/XMI">

    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes" method="xml" />

    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="XMI">
        <xmi:XMI xmlns:xmi="http://www.omg.org/XMI" xmlns:UML="org.omg.xmi.namespace.UML"
            xmi.version="2.1" timestamp="">
            <xsl:apply-templates/>
        </xmi:XMI>
    </xsl:template>

    <xsl:template match="UML:Package">
        <packagedElement xmi:type="umi:package">
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </packagedElement>
    </xsl:template>

    <xsl:template match="UML:Class">
        <packagedElement xmi:type="uml:Class">
            <xsl:copy-of select="@*"/>
            <xsl:choose>
                <xsl:when test="UML:Classifier.feature">
                    <xsl:apply-templates/>
                </xsl:when>
                <xsl:otherwise>
                    <generalization xmi.id="{../@xmi.id}">
                        <general xmi:type="uml:Class" />
                    </generalization>
                </xsl:otherwise>
            </xsl:choose>
        </packagedElement>
    </xsl:template>

    <xsl:template match="UML:Classifier.feature">
        <generalization xmi.id="{../@xmi.id}">
            <general xmi:type="uml:Class" />
        </generalization>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="UML:Attribute">
        <ownedAttribute>
            <xsl:copy-of select="@*"/>
        </ownedAttribute>
    </xsl:template>

    <xsl:template match="XMI.content|UML:Model|UML:Namespace.ownedElement">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="UML:Generalization"/>


</xsl:stylesheet>