首页 > 解决方案 > XSLT 复制节点和更改属性

问题描述

我正在处理一些包含日志记录的非常大的 XML 文件。我有多个具有不同参数的 XML 文件,尽管<record>,<date><message>标记始终存在。

它们看起来像这样(非常简化):

数据1.xml:

<record>
  <date>2018-10-01 00:00:00</date>
  <message>data1</message>
  <param key="Key1">Info</param>
  <param key="Key2">Info</param>
  <param key="Key3">Info</param>
</record>
<record>
  <date>2018-10-02 00:00:00</date>
  <message>data1</message>
  <param key="Key1">Info</param>
  <param key="Key2">Info</param>
  <param key="Key3">Info</param>
</record>

数据2.xml:

<record>
  <date>2018-10-01 00:00:00</date>
  <message>data2</message>
  <param key="Key4">Info</param>
  <param key="Key5">Info</param>
</record>
<record>
  <date>2018-10-02 00:00:00</date>
  <message>data2</message>
  <param key="Key6">Info</param>
  <param key="Key7">Info</param>
</record>

数据3.xml:

<record>
  <date>2018-10-01 00:00:00</date>
  <message>data3</message>
  <param key="Duration(h)">0:00:10</param>
  <attribute1>Info</attribute1>
</record>
<record>
  <date>2018-10-02 00:00:00</date>
  <message>data3</message>
  <param key="Duration(h)">0:01:30</param>
  <attribute1>Info</attribute1>
</record>

我正在使用 XSLT 根据多个变量过滤记录,即可以是日期和消息,或者消息和 key1 等。

在 XSLT 内部,我需要做的就是过滤掉所需的记录,并调用<xsl:apply-imports>其他 XSLT 文件链来管理诸如排序输出、格式化和其他事情之类的事情。

我目前正在过滤出所需的 XML 节点,如下所示:

对于此示例,假设我想要带有<message>data1</message>和的记录<message>data3</message>,并编辑它们的属性以在表格中很好地显示它们。

过滤器1.xsl:

<!-- ignores records not matched by another template, so in this case 'data2' -->
<xsl:template match="record" /> 

<!-- applies imports to 'wanted' data -->
<xsl:template match="record[message='data1']"> 
  <xsl:apply-imports />
</xsl:template>

<!-- applies imports to 'wanted' data -->
<xsl:template match="record[message='data3']"> 
  <xsl:apply-imports />
</xsl:template>

<!-- rename 'param' to match the attribute from 'data3.xml' -->
<xsl:template match="param[@key='key1']">
  <xsl:copy>
    <xsl:attribute>attribute1</xsl:attribute>
    <xsl:value-of select="." />
  </xsl:copy>
</xsl:template>

到目前为止一切顺利,现在回答我的问题。

“data3.xml”中的每条记录都标志着操作的结束,因此是“duration”参数。我想复制每个节点以标记此操作的开始。

所以对于输入:

<!-- marking end of operation -->
<record>
  <date>2018-10-01 00:00:00</date>
  <message>data3</message>
  <param key="Duration(h)">0:00:10</param>
  <attribute1>Info</attribute1>
</record>

我想要输出:

<!-- marking start of operation -->
<record>
  <date>2018-09-30 23:59:50</date>
  <message>data3</message>
  <attribute1>Info</attribute1>
</record>
<!-- marking end of operation -->
<record>
  <date>2018-10-01 00:00:00</date>
  <message>data3</message>
  <param key="Duration(h)">0:00:10</param>
  <attribute1>Info</attribute1>
</record>

但我还没有找到如何复制整个节点并处理它们。这是我到目前为止所尝试的:

调用命名模板根据现有节点属性创建一个新节点,这根本没有插入任何节点:

<xsl:template match="record[message='data3']">
  <xsl:call-template name="duplicateNode"></xsl:call-template>
  <xsl:apply-imports />
</xsl:template>

<xsl:template name="duplicatePrintedNode">
  <xsl:copy>
    <record>
      <date>2018-09-30 23:59:50</date>
      <message>data3</message>
      <attribute1>Info</attribute1>
    </record>
  </xsl:copy>
</xsl:template>

使用 复制整个节点<xsl:copy-of value"">。这似乎在我要复制的现有节点的属性中插入了一个节点:

<xsl:template match="param[@key='Duration(h)']" mode="copy">
  <record>
    <xsl:copy>
      <xsl:copy-of select="@*"/>
    </xsl:copy>
  </record>
</xsl:template>

那么如何复制一个节点,更改属性,然后将模板和导入应用到它们两者?

我在 SO 和 Google 上进行了很多搜索,但该关键字copy似乎在 XSLT 上下文中具有多种含义。任何帮助将不胜感激。

标签: xmlxsltxpathxslt-1.0

解决方案


考虑以下(非常)简化的示例:

XML

<input>
    <record>
        <date>2018-10-01 00:00:00</date>
        <message>data3</message>
        <param key="Duration(h)">0:00:10</param>
        <attribute1>Info</attribute1>
    </record>
</input>

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/input">
    <output>
        <xsl:apply-templates/>
    </output>
</xsl:template>

<xsl:template match="record[message='data3']">
    <xsl:copy>
        <date>new value</date>
        <xsl:copy-of select="node()[not(self::date or self::param)]"/>
    </xsl:copy>
    <xsl:copy>
        <xsl:copy-of select="node()"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

结果

<?xml version="1.0" encoding="UTF-8"?>
<output>
   <record>
      <date>new value</date>
      <message>data3</message>
      <attribute1>Info</attribute1>
   </record>
   <record>
      <date>2018-10-01 00:00:00</date>
      <message>data3</message>
      <param key="Duration(h)">0:00:10</param>
      <attribute1>Info</attribute1>
   </record>
</output>

注意:出于演示的目的,我使用xsl:copy-ofxsl:apply-imports- 因为我们不知道这些导入是什么。


推荐阅读