0

我有一个具有以下结构的 XML 文件(多个“实体”节点):

<!-- entities.xml -->
<root>
    <entity template="foo-template" kind="foo" name="bar">
        <groups>
            <group id="1">
                <definition id="1" name="foobar" />
            </group>
        </groups>
    </entity>
</root>

许多entity节点具有相似的属性和子节点。我想允许用户entity在单独的文件中创建模板。引用模板将按如下方式完成:

<entity template="foo-template" kind="foo" ... />

“foo-template”中的每个属性和子节点都应该复制到 中entity,除了那些已经存在的(即允许覆盖模板)。

我对 XSLT 不是很熟悉。它是完成这项任务的正确工具,还是没有它我最好实现它?

我正在使用 C++ 和 RapidXml,但可以使用其他 XML 库。


编辑:示例

模板文件:

<!-- templates.xml -->
<templates>
    <entity template="foo-template" name="n/a" model="baz">
        <groups>
            <group id="1">
                <definition id="1" name="def1" />
                <definition id="2" name="def2" />
            </group>
            <group id="2">
                <definition id="1" name="def3" />
                <definition id="2" name="def4" />
            </group>
        </groups>
    </entity>
</templates>

输出文件:

<!-- output.xml -->
<root>
    <entity kind="foo" name="bar" model="baz">
        <groups>
            <group id="1">
                <definition id="1" name="foobar" />
            </group>
            <group id="2">
                <definition id="1" name="def3" />
                <definition id="2" name="def4" />
            </group>
        </groups>
    </entity>
</root>

因此输出包含来自“entities.xml”的第 1 组和来自“templates.xml”的第 2 组。无需合并group具有相同 id 的节点。

4

2 回答 2

2

如果你有一个templates.xml看起来像的文件

<templates>
  <entity template="foo-template" kind="foo" name="bar" model="baz" />
  <!-- and other entity elements with different template="..." values -->
</templates>

然后像下面这样的 XSLT 将实现您所追求的

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

  <xsl:key name="kEntityTemplate" match="entity" use="@template" />

  <!-- identity template - copy everything not overridden by another template -->
  <xsl:template match="@*|node">
    <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
  </xsl:template>

  <xsl:template match="entity[@template]">
    <xsl:variable name="thisEntity" select="." />
    <!-- switch to templates doc -->
    <xsl:for-each select="document('templates.xml')">
      <xsl:variable name="template"
         select="key('kEntityTemplate', $thisEntity/@template)" />

      <entity>
        <!-- copy template attributes that are not overridden -->
        <xsl:for-each select="$template/@*">
          <xsl:if test="not($thisEntity/@*[name() = name(current())])">
            <!-- if not, copy the one from the template -->
            <xsl:apply-templates select="." />
          </xsl:if>
        </xsl:for-each>

        <!-- copy source attributes -->
        <xsl:apply-templates select="$thisEntity/@*[name() != 'template']" />

        <!-- deal with elements -->
        <xsl:if test="$thisEntity/groups/group | $template/groups/group">
          <groups>
            <!-- here we select all group elements from the source plus
                 those group elements from the template that do not also exist
                 in the source, and sort the whole lot by id -->
            <xsl:apply-templates select="$thisEntity/groups/group
              | $template/groups/group[not(@id = $thisEntity/groups/group/@id)]">
              <xsl:sort select="@id" data-type="number" />
            </xsl:apply-templates>
          </groups>
        </xsl:if>
      </entity>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

templates.xml文件需要与样式表位于同一目录中。

于 2013-03-05T14:02:56.213 回答
0

除了进行任何类型的 XML 转换之外,您还有一个选择是导入另一个 XML 文件,然后从标记中引用它。有关示例,请参见此处。

这将要求您的用户为您可能不想要的每种模板类型拥有单独的模板文件。但是,由于接吻原则,我更喜欢导入方法。如果您不熟悉 XSLT,那么导入可能也是一种更好的方法。

我希望这有帮助!

于 2013-03-05T13:46:53.533 回答