背景
我正在设计一个Perl应用程序,它使用XML文件作为配置和设置信息的输入。将有一个文档层次结构,全局数据将被更多的本地信息覆盖。
我的程序将使用最本地的设置文件调用,该文件将包含指向更通用文件的路径。一些本地设置将是绝对的,这些设置将在程序中进行硬编码。
初始化任务是从最高级别获取调用的设置,读取它们,然后转到每个级别,并将它们合并/联接为单个XML文档。
示例数据
全局布局_100.xml
<CONFIG>
<GRP1>
<FIELD foo="abs" format="%.4f">QTY</FIELD>
<FIELD default="" format="%.2f">COST</FIELD>
<FIELD default="0" format="%.2f">AMT</FIELD>
<FIELD default="1960-01-01" format="YYYMMDD">TRANDATE</FIELD>
<FIELD>ACCOUNT</FIELD>
<FIELD default="0">ACCT_TYPE</FIELD>
</GRP1>
<GRP2>
<FIELD> 1 </FIELD>
<FIELD> 2 </FIELD>
<FIELD> 3 </FIELD>
</GRP2>
</CONFIG>
全局属性_100.xml
<CONFIG>
<CUS>
<GRP>GRP1</GRP>
<HDR>CUSTOMER</HDR>
<TLR>TLR${cnt}</TLR>
</CUS>
<XYZ>
<GRP>GRP2</GRP>
<HDR>ACCOUNTS</HDR>
<TLR>TLR${cnt}</TLR>
</XYZ>
</CONFIG>
Global_70.xml
<CONFIG>
<PARENT_SETTINGS>Global_layouts_100</PARENT_SETTINGS>
<PARENT_SETTINGS>Global_properties_100</PARENT_SETTINGS>
<LOOKUPS>
<MAP type="file">
<NAME>ACCT_TYPE_LOOKUP</NAME>
<PATH>${PATH}acct_type.csv</PATH>
<HEADERS>
<COLUMN>ACCT_TYPE</COLUMN>
<COLUMN>SOURCE_VALUE</COLUMN>
</HEADERS>
<KEYS>
<COLUMN>SOURCE_VALUE</COLUMN>
</KEYS>
</MAP>
</LOOKUPS>
</CONFIG>
local.xml
<CONFIG>
<PARENT_SETTINGS>Global_70</PARENT_SETTINGS>
<BATCH>
<CUS>
<SRCFILE type="csv" delimiter="|">/path/to/src_file</SRCFILE>
<OUTFILE>/path/to/out_file</OUTFILE>
<FIELDS>
<CUSTOMER>&CUSTOMER;</CUSTOMER>
<QTY default="0.0" col="23"></QTY>
<COST format="%.4f" col="21"></COST>
<FEE col="18"></FEE>
</FIELDS>
</CUS>
<XYZ>
<SRCFILE />
<OUTFILE />
<FIELDS>
<FIELD_1 />
<FIELD_2 />
<FIELD_3 />
<FIELD_4 />
<FIELD_5 />
</FIELDS>
</XYZ>
</BATCH>
</CONFIG>
现在,如果程序将被赋予local.xml来启动,CUS作为arg来处理,我希望看到以下xml(或等价的perl数据结构(:
<CONFIG>
<HDR>CUSTOMER</HDR>
<TLR>TLR${cnt}</TLR>
<SRCFILE type="csv" delimiter="|">/path/to/src_file</SRCFILE>
<OUTFILE>/path/to/out_file</OUTFILE>
<LOOKUPS>
<MAP type="file">
<NAME>ACCT_TYPE_LOOKUP</NAME>
<PATH>${PATH}acct_type.csv</PATH>
<HEADERS>
<COLUMN>ACCT_TYPE</COLUMN>
<COLUMN>SOURCE_VALUE</COLUMN>
</HEADERS>
<KEYS>
<COLUMN>SOURCE_VALUE</COLUMN>
</KEYS>
</MAP>
</LOOKUPS>
<CUS>
<FIELD foo="abs" format="%.4f" default="0.0" col="23">QTY</FIELD>
<FIELD default="" format="%.4f" col="21">COST</FIELD>
<FIELD default="0" format="%.2f">AMT</FIELD>
<FIELD default="1960-01-01" format="YYYMMDD">TRANDATE</FIELD>
<FIELD>ACCOUNT</FIELD>
<FIELD default="0">ACCT_TYPE</FIELD>
<FIELDS>
<CUSTOMER>&CUSTOMER;</CUSTOMER>
<QTY default="0.0" col="23"></QTY>
<COST format="%.4f" col="21"></COST>
<FEE col="18"></FEE>
</FIELDS>
</CUS>
</CONFIG>
而且,如果程序将被赋予local.xml来启动,XYZ作为arg来处理,我希望看到以下xml(或等效的perl数据结构(:
<CONFIG>
<HDR>ACCOUNTS</HDR>
<TLR>TLR${cnt}</TLR>
<SRCFILE />
<OUTFILE />
<LOOKUPS>
<MAP type="file">
<NAME>ACCT_TYPE_LOOKUP</NAME>
<PATH>${PATH}acct_type.csv</PATH>
<HEADERS>
<COLUMN>ACCT_TYPE</COLUMN>
<COLUMN>SOURCE_VALUE</COLUMN>
</HEADERS>
<KEYS>
<COLUMN>SOURCE_VALUE</COLUMN>
</KEYS>
</MAP>
</LOOKUPS>
<XYZ>
<FIELD> 1 </FIELD>
<FIELD> 2 </FIELD>
<FIELD> 3 </FIELD>
<FIELDS>
<FIELD_1 />
<FIELD_2 />
<FIELD_3 />
<FIELD_4 />
<FIELD_5 />
</FIELDS>
</XYZ>
</CONFIG>
问题
合并这些XML文档最有效的方法是什么?
我可以用XML::Simple
返回的数据结构自己完成,或者我应该使用其他一些XML工具?
我希望我的问题足够清楚,不需要示例XML数据。如果你需要看一些东西,我可以贴一些样品。
简而言之,问题是,合并单个XML文档层次结构的最佳方法是什么?
我可以用一些样本数据给您举一个更具体的例子,但在处理这个问题时,我倾向于使用XML::Twig
。
具体来说,XML::Twig
内置了对cut
和paste
的支持,因此您可以构建一个新的文档树,并按照我想要的顺序保留所需的元素。
类似这样的东西:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $twig = XML::Twig -> parse ( *DATA );
my $newdoc = XML::Twig -> new ('pretty_print' => 'indented_a');
$newdoc -> set_root ( XML::Twig::Elt -> new ( 'new_root_here' ) );
$newdoc -> set_xml_version ('1.0');
$newdoc -> set_encoding('utf-8');
foreach my $value_elt ( $twig -> findnodes ( '//value' ) ) {
$value_elt -> cut;
$value_elt -> paste ( $newdoc -> root );
}
$newdoc -> print;
__DATA__
<root>
<value>fish</value>
<dont_copy>this thing</dont_copy>
</root>
(还有另一个例子:如何将两个XML文件中的数据组合到同一结构中?(
前言:我对Perl一无所知,但有一种选择是使用XSLT,这是一种用于样式化/转换XML文档的声明性专用语言。
我知道,像PHP(某种程度上是Perl的派生(、Python、Java、C#等大多数语言都维护XML库和XSLT转换。因此,考虑应用PerlXSLT处理器,使用XSLT文件合并文档(可以指定特定节点(
使用您的示例数据,下面的样式表将呈现CUS和XYZ的最终XML结构。请确保将所有派生XML保存在同一目录中。
CUS版本
<?xml version="1.0" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="CONFIG">
<xsl:copy>
<xsl:copy-of select="document('Global_properties_100.xml')/CONFIG/CUS/HDR" />
<xsl:copy-of select="document('Global_properties_100.xml')/CONFIG/CUS/TLR" />
<xsl:copy-of select="BATCH/CUS/SRCFILE" />
<xsl:copy-of select="BATCH/CUS/OUTFILE" />
<xsl:copy-of select="document('Global_70.xml')/CONFIG/LOOKUPS" />
<CUS>
<xsl:copy-of select="document('Global_layouts_100.xml')/CONFIG/GRP1/*" />
<xsl:copy-of select="BATCH/CUS/FIELDS" />
</CUS>
</xsl:copy>
</xsl:template>
</xsl:transform>
XYZ版本
<?xml version="1.0" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="CONFIG">
<xsl:copy>
<xsl:copy-of select="document('Global_properties_100.xml')/CONFIG/XYZ/HDR" />
<xsl:copy-of select="document('Global_properties_100.xml')/CONFIG/XYZ/TLR" />
<xsl:copy-of select="BATCH/XYZ/SRCFILE" />
<xsl:copy-of select="BATCH/XYZ/OUTFILE" />
<xsl:copy-of select="document('Global_70.xml')/CONFIG/LOOKUPS" />
<CUS>
<xsl:copy-of select="document('Global_layouts_100.xml')/CONFIG/GRP2/*" />
<xsl:copy-of select="BATCH/XYZ/FIELDS" />
</CUS>
</xsl:copy>
</xsl:template>
</xsl:transform>