我必须使用SAS数据集生成XML文件。 XML文件的格式是非常严格定义的,我需要完全匹配它。我使用SAS 9.4(注意:并坚持使用它!)并使用XMLMAP和libname xmlv2。我觉得我非常接近解决方案,但这是我无法通过的最后一道障碍!
XML文件具有3级结构,其中包含一个包含所有lvl 3元素的lvl 2元素。无论我尝试什么,我所有的lvl 3元素似乎都会产生他们的自己的 lvl 2元素。在导入或导出完全相同的数据时,SAS xmlv2 libname引擎似乎有所不同!例子&下面重现的步骤 - 如果可以,请帮助我!
示例数据
数据是文件列表和与这些文件相关的一些属性。这些属性对所有文件都是通用的,只有列表中的文件名不同。这会在SAS工作中创建一个测试数据集:
proc sql;
create table input_data
(col1 char(1),
col2 char(1),
file char(20));
insert into input_data
values ('1', '2', 'file1.txt')
values ('1', '2', 'file2.txt');
quit;
所需的输出XML
请注意,所有文件名都在它们自己的FILE元素中列出,嵌套在单个FILES元素中。公共属性是主FILE_INFO元素内的元素。这是我需要能够输出的结构。
<?xml version="1.0" encoding="windows-1252" ?>
<FILE_INFO>
<COL1>1</COL1>
<COL2>2</COL2>
<FILES>
<FILE>file1.txt</FILE>
<FILE>file2.txt</FILE>
</FILES>
</FILE_INFO>
SAS XMLMAP我已创建
<?xml version="1.0" encoding="windows-1252"?>
<!-- ############################################################ -->
<!-- this is a map file for SAS-XML conversion -->
<!-- ############################################################ -->
<SXLEMAP name="file_test" version="2.1">
<!-- ############################################################ -->
<OUTPUT>
<TABLEREF name="FILE_INFO"/>
</OUTPUT>
<NAMESPACES count="0"/>
<!-- ############################################################ -->
<TABLE name="FILE_INFO">
<TABLE-PATH syntax="XPath">/FILE_INFO/FILES/FILE</TABLE-PATH>
<COLUMN name="col1" retain="YES">
<PATH syntax="XPath">/FILE_INFO/COL1</PATH>
<TYPE>character</TYPE>
<DATATYPE>string</DATATYPE>
<LENGTH>1</LENGTH>
</COLUMN>
<COLUMN name="col2" retain="YES">
<PATH syntax="XPath">/FILE_INFO/COL2</PATH>
<TYPE>character</TYPE>
<DATATYPE>string</DATATYPE>
<LENGTH>1</LENGTH>
</COLUMN>
<COLUMN name="file">
<PATH syntax="XPath">/FILE_INFO/FILES/FILE</PATH>
<TYPE>character</TYPE>
<DATATYPE>string</DATATYPE>
<LENGTH>20</LENGTH>
</COLUMN>
</TABLE>
</SXLEMAP>
使用XMLMAP输出XML的SAS代码
filename out "C:\myfolder\test_out.xml";
libname out xmlv2 xmltype=xmlmap xmlmap="C:\myfolder\file_test.map";
data out.FILE_INFO;
set work.input_data;
run;
实际结果XML
<?xml version="1.0" encoding="windows-1252" ?>
<FILE_INFO>
<FILES>
<FILE>file1.txt</FILE>
</FILES>
<COL1>1</COL1>
<COL2>2</COL2>
<FILES>
<FILE>file2.txt</FILE>
</FILES>
</FILE_INFO>
重现的步骤
使用上面的代码生成测试数据集。将XMLMAP保存到file_test.map中。运行SAS代码,将生成的XML与所需结果进行比较。
问题
看看那里发生了什么?所有FILE元素都在自己的FILES元素中。无论我在数据中有多少个具有单独文件名的行,都会发生这种情况:每个文件名都有自己的FILES元素。
有趣的是,如果我使用上面所需的输出XML文件,并使用相同的XMLMAP将其返回提供给SAS,则生成的SAS数据集与我的原始输入数据集完全相同!
我尝试在XMLMAP中摆弄RETAIN选项,我尝试将FILES定义为输入数据集中的自己的列并在XMLMAP中定义它,我尝试了各种各样的随机的东西但无济于事。有什么想法吗?
答案 0 :(得分:4)
因为您所需的XML涉及一些有点复杂的分组,请考虑XSLT,这是专门用于转换XML文件的专用语言。 SAS 9.4使用带有proc xsl的Saxon-EE版本9.3引擎维护一个XSLT处理器,该引擎允许使用XSLT 1.0或2.0脚本。
具体来说,将数据导出到原始xml文件(无映射)并使用XSLT 1.0的Muenchian Grouping或更简单的XSLT 2.0 xsl:for-each-group
。我包括两者,因为为了便携性,XSLT 1.0被广泛用作其他语言库(Java,Python,PHP,R)中的默认规范,以防您需要在SAS之外运行或未来的读者使用早期版本。
请注意,您将看到 cols 在concat()
内的XSLT中以及指定模板中的<COL>
节点中进行了硬编码。对于其他cols,请相应地添加到这些部分。由于SAS在文本值之前/之后填充空格,因此使用normalize-space()
。
XSLT 1.0 (另存为.xsl文件)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="colkeys" match="INPUT_DATA" use="concat(col1, col2)" />
<xsl:template match="/TABLE">
<FILE_INFO>
<xsl:apply-templates select="INPUT_DATA[generate-id() =
generate-id(key('colkeys', concat(col1, col2)))]"/>
</FILE_INFO>
</xsl:template>
<xsl:template match="INPUT_DATA">
<COL1><xsl:value-of select="normalize-space(col1)"/></COL1>
<COL2><xsl:value-of select="normalize-space(col2)"/></COL2>
<FILES>
<xsl:for-each select="key('colkeys', concat(col1, col2))">
<FILE><xsl:value-of select="normalize-space(file)"/></FILE>
</xsl:for-each>
</FILES>
</xsl:template>
</xsl:stylesheet>
XSLT 2.0 (另存为.xsl文件)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="colkeys" match="INPUT_DATA" use="concat(col1, col2)" />
<xsl:template match="/TABLE">
<FILE_INFO>
<xsl:for-each-group select="INPUT_DATA" group-by="concat(col1, col2)">
<COL1><xsl:value-of select="normalize-space(col1)"/></COL1>
<COL2><xsl:value-of select="normalize-space(col2)"/></COL2>
<FILES>
<xsl:for-each select="current-group()">
<FILE><xsl:value-of select="normalize-space(file)"/></FILE>
</xsl:for-each>
</FILES>
</xsl:for-each-group>
</FILE_INFO>
</xsl:template>
</xsl:stylesheet>
<强> SAS 强>
** EXPORT DATASET TO XML FILE;
filename out "C:\Path\Raw_Output.xml";
libname out xml;
data out.input_data;
set Work.input_data;
run;
libname out clear;
proc xsl
in="C:\Path\Raw_Output.xml"
out="C:\Path\Final_Output.xml"
xsl="C:\Path\XSLT_Script.xsl";
run;
<强>输出强>
<?xml version="1.0" encoding="UTF-8"?>
<FILE_INFO>
<COL1>1</COL1>
<COL2>2</COL2>
<FILES>
<FILE>file1.txt</FILE>
<FILE>file2.txt</FILE>
</FILES>
</FILE_INFO>