XSLT复制节点和更改属性

时间:2018-11-28 10:39:24

标签: xml xslt xpath xslt-1.0

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

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

data1.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>

data2.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>

data3.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>的记录,并编辑它们的属性以将它们很好地显示在表中。

filter1.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上下文中似乎具有多种含义。任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:1)

考虑以下(很多)简化示例:

XML

$(this)

XSLT 1.0

<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>

结果

<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-of-因为我们不知道这些进口是什么。

答案 1 :(得分:0)

我找到了一种方法,我意识到这样做的教育价值非常低,因为我的情况是如此具体,但是无论如何我都会发布它。也要感谢@ michael.hor257k的帮助。

我复制特定节点并对两个节点应用导入的方式是:

filter1.xsl

<script>
    $(document).ready(function() {
        $("div.text-center").css("color", "red");
    });
</script>