【问题标题】:add data to an element, or create one if it doesnot exist, based on record matching criteria根据记录匹配条件将数据添加到元素,如果不存在则创建元素
【发布时间】:2017-09-21 13:51:50
【问题描述】:

给定以下 xml 文件作为输入:

<?xml ="1.0" encoding="UTF-8"?>
<TABLE NAME="TABLE.DB">
    <DATA RECORDS="2">
        <RECORD ID="1">
            <RECNO>1</RECNO>
            <SEQ>0</SEQ>
            <DATE>17/12/1999 2:44:08 μμ</DATE>
            <ID>12/11/2015 3:15:25 μμ</ID>
            <NUMBER>10355</NUMBER>
            <CN>PL</CN>
            <PROPERTY>0</PROPERTY>
            <DAYS>0</DAYS>
            <CURRENTSTATUS>0</CURRENTSTATUS>
            <TOTALS>1</TOTALS>
            <SUB_A>Some (random) data.</SUB_A>
        </RECORD>
        <RECORD ID="2">
            <RECNO>2</RECNO>
            <SEQUENCE>0</SEQUENCE>
            <DATE>17/12/1999 2:44:08 μμ</DATE>
            <ID>12/11/2015 3:15:25 μμ</ID>
            <NUMBER>10356</NUMBER>
            <CN>PL 300 L</CN>
            <PROPERTY>0</PROPERTY>
            <DAYS>10</DAYS>
            <CURRENTSTATUS>0</CURRENTSTATUS>
            <TOTALS>19837</TOTALS>
        </RECORD>
        <RECORD ID="3">
            <RECNO>3</RECNO>
            <SEQUENCE>0</SEQUENCE>
            <DATE>17/12/1999 2:44:08 μμ</DATE>
            <ID>12/11/2015 3:15:25 μμ</ID>
            <NUMBER>10357</NUMBER>
            <CN>PL 300 L</CN>
            <PROPERTY>0</PROPERTY>
            <DAYS>10</DAYS>
            <CURRENTSTATUS>0</CURRENTSTATUS>
            <SUB_A>Some [more] data.</SUB_A>
        </RECORD>
    </DATA>
</TABLE>

我想根据以下条件将数据插入到 SUB_A 元素中: 我有一个制表符分隔的文件,

value A NUMBER  TOTALS

其中值 A 必须在 SUB_A 元素中输入,并带有 |作为分隔符,如果 SUB_A 已经存在于记录中,或者要创建,如果它不存在。 NUMBER 和 TOTALS 是我们需要的,以便在将发生转换的记录上进行匹配。有时,在我们的输入 txt 文件中,值 TOTALS 可能根本不存在:

所以我们的 transform.txt 文件看起来像:

编辑:

请先生使用解决方案。 Honnen 建议,输入数据建议,当我有输入时:

value i'd like to add   10355   1
another) value i'd like to add  10357   19837
yet another value.] 10358
test    10354

我收到以下错误:

       Tree size: 113 nodes, 392 characters, 7 attributes
<line number="10355" totals="1">value i'd like to add</line><line number="10357" totals="19837">another) value i'd like to add</line><line number="10358" totals="">yet another value.]</line><line number="10354" totals="">test</line>
Error at char 6 in xsl:merge-source/@select on line 26 column 65 of merge.xsl:
  XTDE2220: Merge input for source line is not ordered according to merge key, detected at
  key value: ["10354", ""]
  in built-in template rule for /TABLE/DATA[1] in the unnamed mode
  at xsl:apply-templates (/merge.xsl#21)
     processing /TABLE
Merge input for source line is not ordered according to merge key, detected at key value: ["10354", ""]

预期结果:

<?xml ="1.0" encoding="UTF-8"?>
<TABLE NAME="TABLE.DB">
    <DATA RECORDS="2">
        <RECORD ID="1">
            <RECNO>1</RECNO>
            <SEQ>0</SEQ>
            <DATE>17/12/1999 2:44:08 μμ</DATE>
            <ID>12/11/2015 3:15:25 μμ</ID>
            <NUMBER>10355</NUMBER>
            <CN>PL</CN>
            <PROPERTY>0</PROPERTY>
            <DAYS>0</DAYS>
            <CURRENTSTATUS>0</CURRENTSTATUS>
            <TOTALS>1</TOTALS>
            <SUB_A>Some (random) data. | value i'd like to add</SUB_A>
        </RECORD>
        <RECORD ID="2">
            <RECNO>2</RECNO>
            <SEQUENCE>0</SEQUENCE>
            <DATE>17/12/1999 2:44:08 μμ</DATE>
            <ID>12/11/2015 3:15:25 μμ</ID>
            <NUMBER>10356</NUMBER>
            <CN>PL 300 L</CN>
            <PROPERTY>0</PROPERTY>
            <DAYS>10</DAYS>
            <CURRENTSTATUS>0</CURRENTSTATUS>
            <TOTALS>19837</TOTALS>
            <SUB_A>(another) value i'd like to add</SUB_A>
        </RECORD>
        <RECORD ID="3">
            <RECNO>3</RECNO>
            <SEQUENCE>0</SEQUENCE>
            <DATE>17/12/1999 2:44:08 μμ</DATE>
            <ID>12/11/2015 3:15:25 μμ</ID>
            <NUMBER>10357</NUMBER>
            <CN>PL 300 L</CN>
            <PROPERTY>0</PROPERTY>
            <DAYS>10</DAYS>
            <CURRENTSTATUS>0</CURRENTSTATUS>
            <SUB_A>Some [more] data. | [yet another value.]</SUB_A>
        </RECORD>
    </DATA>
</TABLE>

我已经尝试过,会使问题变得更大,并且没有让我接近我所寻找的...

【问题讨论】:

  • 这些数值是否像示例中的那样是 ordere3d ?您使用哪种 XSLT 2.0 处理器?虽然有多种策略可以解决,但这对于 Saxon 9.8 和新的xsl:merge 元素来说可能是一个很好的问题。一般来说,我首先建议使用unparsed-text(-lines)tokenizeanalyze-string 将制表符分隔的数据转换为XML。
  • 数值是按照我做例子的顺序排列的,但是显然在真实的xml中,也有根本不匹配的记录,所以在我选择的记录之间,有还有其他不受影响的记录 - 转换。我更喜欢这样的指导,我使用的是 saxon,是的 9.8 版,所以我应该首先转换输入文件,或者可以在同一个 xsl 样式表中完成?
  • 文本数据到 XML 的转换可以在合并数据的同一个样式表中完成。至于实际数据和可能丢失的TOTALS,在 XML 输入数据和制表符分隔的数据中是否会丢失?如果 RECORD 有某个 NUMBER 与某行匹配,并且 TOTALS 存在于 RECORD 但该行中不存在,会发生什么情况?

标签: xml xslt-2.0


【解决方案1】:

所以我尝试使用来自XSLT 3.0xsl:merge 来实现这一点,处理缺少总计值的可能性并不容易,因为这样你就不能在比较的那部分使用合并键.此外,如果制表符字符之外还有任何空格,则将文本数据转换为 XML 数据也容易混淆合并,例如,在您的示例制表符数据中,最后一个数字后面有空格,这会弄乱合并键,虽然这可以通过使用 normalize-space 来解决,或者通过使用 analyze-string 而不是 tokenize 来提取数据可能更好。

我想出的是

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math" exclude-result-prefixes="xs math"
    expand-text="yes" version="3.0">

    <xsl:param name="text-uri" as="xs:string">test2017092101.txt</xsl:param>

    <xsl:mode on-no-match="shallow-copy"/>

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

    <xsl:variable name="lines" as="element(line)*">
        <xsl:apply-templates select="unparsed-text-lines($text-uri)"/>
    </xsl:variable>

    <xsl:template match=".[. instance of xs:string]">
        <xsl:variable name="tokens" as="xs:string*" select="tokenize(., '&#9;')[normalize-space()]"/>
        <line number="{$tokens[2]}" totals="{$tokens[3]}">{$tokens[1]}</line>
    </xsl:template>

    <xsl:template match="TABLE/DATA">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:merge>
                <xsl:merge-source name="record" select="RECORD">
                    <xsl:merge-key select="NUMBER"/>
                    <xsl:merge-key select="string(TOTALS)"/>
                </xsl:merge-source>
                <xsl:merge-source name="line" select="$lines">
                    <xsl:merge-key select="@number"/>
                    <xsl:merge-key select="string(@totals)"/>
                </xsl:merge-source>
                <xsl:merge-action>
                    <xsl:if test="current-merge-group('record')">
                        <xsl:copy>
                            <xsl:apply-templates select="* except SUB_A"/>
                            <SUB_A>
                                <xsl:value-of select="SUB_A, current-merge-group()[2]" separator="|"
                                />
                            </SUB_A>
                        </xsl:copy>
                    </xsl:if>
                </xsl:merge-action>
            </xsl:merge>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

我试过的一个示例是

value i'd like to add   10355   1
(another) value i'd like to add 10357   19837
[yet another value.]    10358

输入为

<?xml version="1.0" encoding="UTF-8"?>
<TABLE NAME="TABLE.DB">
    <DATA RECORDS="2">
        <RECORD ID="0">
            <RECNO>0</RECNO>
            <SEQ>0</SEQ>
            <DATE>17/12/1999 2:44:08 μμ</DATE>
            <ID>12/11/2015 3:15:25 μμ</ID>
            <NUMBER>10354</NUMBER>
            <CN>PL</CN>
            <PROPERTY>0</PROPERTY>
            <DAYS>0</DAYS>
            <CURRENTSTATUS>0</CURRENTSTATUS>
            <TOTALS>1</TOTALS>
            <SUB_A>Some random data not matched.</SUB_A>
        </RECORD>
        <RECORD ID="1">
            <RECNO>1</RECNO>
            <SEQ>0</SEQ>
            <DATE>17/12/1999 2:44:08 μμ</DATE>
            <ID>12/11/2015 3:15:25 μμ</ID>
            <NUMBER>10355</NUMBER>
            <CN>PL</CN>
            <PROPERTY>0</PROPERTY>
            <DAYS>0</DAYS>
            <CURRENTSTATUS>0</CURRENTSTATUS>
            <TOTALS>1</TOTALS>
            <SUB_A>Some (random) data.</SUB_A>
        </RECORD>
        <RECORD ID="2">
            <RECNO>2</RECNO>
            <SEQUENCE>0</SEQUENCE>
            <DATE>17/12/1999 2:44:08 μμ</DATE>
            <ID>12/11/2015 3:15:25 μμ</ID>
            <NUMBER>10356</NUMBER>
            <CN>PL 300 L</CN>
            <PROPERTY>0</PROPERTY>
            <DAYS>10</DAYS>
            <CURRENTSTATUS>0</CURRENTSTATUS>
            <SUB_A>Some random data not matched.</SUB_A>
        </RECORD>
        <RECORD ID="3">
            <RECNO>3</RECNO>
            <SEQUENCE>0</SEQUENCE>
            <DATE>17/12/1999 2:44:08 μμ</DATE>
            <ID>12/11/2015 3:15:25 μμ</ID>
            <NUMBER>10357</NUMBER>
            <CN>PL 300 L</CN>
            <PROPERTY>0</PROPERTY>
            <DAYS>10</DAYS>
            <CURRENTSTATUS>0</CURRENTSTATUS>
            <TOTALS>19837</TOTALS>
        </RECORD>
        <RECORD ID="3">
            <RECNO>3</RECNO>
            <SEQUENCE>0</SEQUENCE>
            <DATE>17/12/1999 2:44:08 μμ</DATE>
            <ID>12/11/2015 3:15:25 μμ</ID>
            <NUMBER>10358</NUMBER>
            <CN>PL 300 L</CN>
            <PROPERTY>0</PROPERTY>
            <DAYS>10</DAYS>
            <CURRENTSTATUS>0</CURRENTSTATUS>
            <SUB_A>Some [more] data.</SUB_A>
        </RECORD>
    </DATA>
</TABLE>

然后结果是

<?xml version="1.0" encoding="UTF-8"?>
<TABLE NAME="TABLE.DB">
   <DATA RECORDS="2">
      <RECORD>
         <RECNO>0</RECNO>
         <SEQ>0</SEQ>
         <DATE>17/12/1999 2:44:08 μμ</DATE>
         <ID>12/11/2015 3:15:25 μμ</ID>
         <NUMBER>10354</NUMBER>
         <CN>PL</CN>
         <PROPERTY>0</PROPERTY>
         <DAYS>0</DAYS>
         <CURRENTSTATUS>0</CURRENTSTATUS>
         <TOTALS>1</TOTALS>
         <SUB_A>Some random data not matched.</SUB_A>
      </RECORD>
      <RECORD>
         <RECNO>1</RECNO>
         <SEQ>0</SEQ>
         <DATE>17/12/1999 2:44:08 μμ</DATE>
         <ID>12/11/2015 3:15:25 μμ</ID>
         <NUMBER>10355</NUMBER>
         <CN>PL</CN>
         <PROPERTY>0</PROPERTY>
         <DAYS>0</DAYS>
         <CURRENTSTATUS>0</CURRENTSTATUS>
         <TOTALS>1</TOTALS>
         <SUB_A>Some (random) data.|value i'd like to add</SUB_A>
      </RECORD>
      <RECORD>
         <RECNO>2</RECNO>
         <SEQUENCE>0</SEQUENCE>
         <DATE>17/12/1999 2:44:08 μμ</DATE>
         <ID>12/11/2015 3:15:25 μμ</ID>
         <NUMBER>10356</NUMBER>
         <CN>PL 300 L</CN>
         <PROPERTY>0</PROPERTY>
         <DAYS>10</DAYS>
         <CURRENTSTATUS>0</CURRENTSTATUS>
         <SUB_A>Some random data not matched.</SUB_A>
      </RECORD>
      <RECORD>
         <RECNO>3</RECNO>
         <SEQUENCE>0</SEQUENCE>
         <DATE>17/12/1999 2:44:08 μμ</DATE>
         <ID>12/11/2015 3:15:25 μμ</ID>
         <NUMBER>10357</NUMBER>
         <CN>PL 300 L</CN>
         <PROPERTY>0</PROPERTY>
         <DAYS>10</DAYS>
         <CURRENTSTATUS>0</CURRENTSTATUS>
         <TOTALS>19837</TOTALS>
         <SUB_A>(another) value i'd like to add</SUB_A>
      </RECORD>
      <RECORD>
         <RECNO>3</RECNO>
         <SEQUENCE>0</SEQUENCE>
         <DATE>17/12/1999 2:44:08 μμ</DATE>
         <ID>12/11/2015 3:15:25 μμ</ID>
         <NUMBER>10358</NUMBER>
         <CN>PL 300 L</CN>
         <PROPERTY>0</PROPERTY>
         <DAYS>10</DAYS>
         <CURRENTSTATUS>0</CURRENTSTATUS>
         <SUB_A>Some [more] data.|[yet another value.]</SUB_A>
      </RECORD>
   </DATA>
</TABLE>

当然也应该可以使用分组而不是合并来解决这个问题。将表格数据转换为 XML 的代码将保持不变。

这有点混乱,但为了不在 cmets 中交换代码,请尝试替换模板以创建 line 元素

<xsl:template match=".[. instance of xs:string]">
    <xsl:variable name="groups" select="analyze-string(., '([^0-9]*?)\s*([0-9]+)(\s*([0-9]+))?')//*:group"/>
    <line number="{$groups[2]}" totals="{$groups[4]}">{$groups[1]}</line>
</xsl:template>

尝试使用analyze-string 而不是tokenize,但假设第一列不包含任何数字,第二列和可选的第三列由数字组成。

【讨论】:

  • @sadoyc,我已经添加了我测试的示例数据,如果您的数据不同,请尝试检查并调整代码,我不会下载 rar 文件。如果您仍然有问题,请尝试使用最少的示例来编辑您的问题,以尝试说明问题。
  • 问题出在我这边,使用 .net 版本,在 Windows 10 版本 9.8HE 上,使用您的示例数据,并且 xml 不受影响
  • 您是否将参数&lt;xsl:param name="text-uri" as="xs:string"&gt;test2017092101.txt&lt;/xsl:param&gt;设置为文本文件的文件名或URL?能否添加一个模板&lt;xsl:template match="/"&gt;&lt;xsl:message select="$lines"/&gt;&lt;xsl:apply-templates/&gt;&lt;/xsl:template&gt; 来检查文本文件是否正确转换为XML 元素?
  • 我想添加的值 10355 1(另一个)我想添加的值喜欢添加 10357 19837[还有另一个值。] 10358,在添加好建议的模板后,我在控制台上得到输出,正好在 test2017092101.txt
  • 所以不知何故,标记化是脆弱的,至少在从或复制样本到 stackoverflow 帖子时。我认为通过使用analyze-string 而不是tokenize,将文本转换为XML 的过程可能会更加健壮,但是到目前为止,该文本的整个格式都没有被指定为编写一个好的正则表达式模式。两个数字前面的文字可以包含任何数字吗?
猜你喜欢
  • 2018-05-04
  • 2020-10-25
  • 1970-01-01
  • 2022-10-14
  • 2016-07-02
  • 1970-01-01
  • 2017-03-19
  • 1970-01-01
  • 2022-07-29
相关资源
最近更新 更多