【问题标题】:Update Specific Node of XML file based on match from data in array根据数组中数据的匹配更新 XML 文件的特定节点
【发布时间】:2017-03-09 00:30:49
【问题描述】:

我对 Perl 还很陌生。我想更新此 XML 文件中的特定节点值 LocationID,它与我从文本文件中读取的值匹配。

示例 XML 文件

<?xml version="1.0" encoding="UTF-8"?>
<TestImportFile xmlns="urn:TestImportFile-schema">
    <LOCATION SOURCEID="Yes">
        <LOCATIONID>F16-000100</LOCATIONID>
        <LOCATIONCATEGORY>UFO ABDUCTEE</LOCATIONCATEGORY>
        <LOCAL BIT="Test Case File">
            <LOCALNAME>DTG2QP</LOCALNAME>
            <ASSIGNEDTO>BearmanJ</ASSIGNEDTO>
            <ASSIGNEDTODATETIME>2016-02-02T07:59:00</ASSIGNEDTODATETIME>
            <CASE>
                <CASEVALUE>21</CASEVALUE>
            </CASE>
            <CASE>
                <CASEVALUE>35</CASEVALUE>
            </CASE>
        </LOCAL>
        <LOCAL BIT="Test Case File">
            <LOCALNAME>F4T2557</LOCALNAME>
            <READINGBY>BearmanJ</READINGBY>
            <READINGDATETIME>2016-04-03T06:48:00</READINGDATETIME>
            <CASE>
                <CASEVALUE>83</CASEVALUE>
            </CASE>
            <CASE>
                <CASEVALUE>40</CASEVALUE>
            </CASE>
        </LOCAL>
    </LOCATION>
    <LOCATION SOURCEID="Yes">
        <LOCATIONID>F16-000101</LOCATIONID>
        <LOCATIONCATEGORY>UFO ABDUCTEE</LOCATIONCATEGORY>
        <LOCAL BIT="Test Case File">
            <LOCALNAME>ZGV4TF</LOCALNAME>
            <ASSIGNEDTO>BearmanJ</ASSIGNEDTO>
            <ASSIGNEDTODATETIME>2016-02-02T07:59:00</ASSIGNEDTODATETIME>
            <CASE>
                <CASEVALUE>34</CASEVALUE>
            </CASE>
            <CASE>
                <CASEVALUE>67</CASEVALUE>
            </CASE>
        </LOCAL>
        <LOCAL BIT="Test Case File">
            <LOCALNAME>E5Y7456</LOCALNAME>
            <READINGBY>BearmanJ</READINGBY>
            <READINGDATETIME>2016-04-03T06:48:00</READINGDATETIME>
            <CASE>
                <CASEVALUE>53</CASEVALUE>
            </CASE>
            <CASE>
                <CASEVALUE>20</CASEVALUE>
            </CASE>
        </LOCAL>
    </LOCATION>
    <LOCATION SOURCEID="Yes">
        <LOCATIONID>F16-000102</LOCATIONID>
        <LOCATIONCATEGORY>UFO ABDUCTEE</LOCATIONCATEGORY>
        <LOCAL BIT="Test Case File">
            <LOCALNAME>ZGV4TF</LOCALNAME>
            <ASSIGNEDTO>BearmanJ</ASSIGNEDTO>
            <ASSIGNEDTODATETIME>2016-02-02T07:59:00</ASSIGNEDTODATETIME>
            <CASE>
                <CASEVALUE>34</CASEVALUE>
            </CASE>
            <CASE>
                <CASEVALUE>67</CASEVALUE>
            </CASE>
        </LOCAL>
        <LOCAL BIT="Test Case File">
            <LOCALNAME>E5Y7456</LOCALNAME>
            <READINGBY>BearmanJ</READINGBY>
            <READINGDATETIME>2016-04-03T06:48:00</READINGDATETIME>
            <CASE>
                <CASEVALUE>53</CASEVALUE>
            </CASE>
            <CASE>
                <CASEVALUE>20</CASEVALUE>
            </CASE>
        </LOCAL>
    </LOCATION>
</TestImportFile>

示例文本文件

  F16-000100:2B-16-NOR-0005-J3
  F16-000101:2B-16-NOR-0005-J4
  F16-000102:2B-16-NOR-0005-J5

我可以将测试文件读入一个数组,但我无法确定如何在 XML 文件中搜索匹配项,然后将 XML 文件中的值更新为所需的值。

我要在文本文件中读取的脚本:

my $filename = '1TestData.txt';
open(FILE, $filename) or die "Could not read from $filename, program    halting.";
my $output = '1TestOutput.txt';
open(OUTPUT, '>'.$output) or die "Can't create $output.\n";
while(<FILE>){
    chomp;
    @fields = split(':', $_);
    print "$fields[0]\n";
}
close FILE;

我想将LOCATIONID 值更新为在文本文件中找到的匹配值的第二个值。

<LOCATIONID>F16-000100</LOCATIONID>

期望的结果:

<LOCATIONID>2B-16-NOR-0005-J3</LOCATIONID>

不涉及 XML 文件中的任何其他内容。

【问题讨论】:

  • LOCATIONID 是唯一的还是重复的?
  • locationid 是唯一的,但在这个 xml 文件中可能有 1000 到 3000 个。文本文件也一样。应该是一对一的匹配。
  • 好的。我给出的示例应该可以工作,但是如果您的 XML 特别大,您可能会遇到内存问题。如果你这样做了,那么我们可以使用另一种方法(增量解析),但除非有必要,否则我不建议这样做。
  • 优秀。太感谢了。我将在这个文件上尝试 1000 个位置;如果可行,我可以将文件限制为该大小。这个脚本一周左右只运行一次。
  • 我认为内存问题出现了,因为屏幕输出没有完成。除了最终的 xml 文件外,我真的不需要查看结果。我可以在那里验证。增量解析方法会是什么样子?为什么你会反对它。

标签: xml perl text


【解决方案1】:

请 - 不要使用正则表达式。 XML 是上下文相关的,而正则表达式......不是。

考虑到这一点 - 使用解析器。我喜欢XML::TwigXML::LibXML也不错。XML::Simple就是discouraged

但是您确实有xpath 可用,这很相似,但更适合它。

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
use Data::Dumper;


#parse your file.
my $xml = XML::Twig -> new -> parsefile('sample1.xml');

#open the replacements file for reading
open ( my $input, '<', 'file2.txt') or die $!;
#turn it into key-values for replacement
#probably a bit overkill, as you can just do this iteratively instead. 
my %replace = map { s/\s+//g; split /:/ } <$input>;
close ( $input );

#print for debug
print "Using for replacement:\n ";
print Dumper \%replace;

#iterate all of the search terms
foreach my $search ( keys %replace ) { 
   #use XPATH to find location ID that matches.
   #note - this only finds the _first_ location ID. To do 'all' you'd 
   #need to loop. 
   $xml -> get_xpath("//LOCATIONID[string()=\"$search\"]",0) -> set_text($replace{$search});
}

#set output formatting
$xml -> set_pretty_print('indented_a');
#print to screen
$xml -> print;

#for output:
open ( my $output, '>', 'transformed.xml' ) or die $!;
print {$output} $xml -> sprint;
close ( $output );

如果有多个特定位置 ID 实例,您需要:

$_ -> set_text($replace{$search}) for $xml -> get_xpath("//LOCATIONID[string()=\"$search\"]");

相反,因为这将搜索与该特定 ID 匹配的 所有 个节点并替换所有节点。

【讨论】:

  • 肯定有多个位置ID。 xml 文件将包含大约 1000 到 3000 个位置 ID。
  • 有效! Sobrique 谢谢你,像冠军一样工作!非常感谢。
  • 抱歉,“不唯一”中的多个 - 多个匹配特定键。
  • 匹配特定键的倍数。您的解决方案似乎正在工作并且完全符合我的要求。我即将测试一个包含 1000 条记录的 txt 文件;使用循环。
猜你喜欢
  • 2021-02-18
  • 2018-06-02
  • 1970-01-01
  • 2015-01-07
  • 2014-02-01
  • 2015-01-18
  • 1970-01-01
  • 2019-01-11
  • 1970-01-01
相关资源
最近更新 更多