XMLV2 库创建的映射文件可以在使用 libref 将数据复制到 SAS 会话之前进行修改。
引擎生成的地图文件(本身就是一个xml文件)的处理方式有很多种
- XSL 转换 (Proc XSL)
- 精通 XSLT 语言的人(不是我)可能会编写一个简短的程序来执行修改
- 已解析的 xml 文档的编程操作
- xml 文件的文本操作
地图文件:
- 定义哪些 xml 节点将成为数据集列
- 定义要创建的表
- 定义哪些表要包含哪些列
对于想要将 XMLV2 引擎对数字列的解释更改为字符列的情况,需要修改映射文件(又名 transformed)。
-
<COLUMN> 节点具有至少需要更改的子节点
来自
<TYPE>numeric</TYPE>
到
<TYPE>character</TYPE>
注意事项
在将数字转换为字符集时,您可能还需要考虑其他一些因素,例如:
- 这个数字是日期吗?
- 是否应该先通过其格式呈现数字?
SAS 用户对包含数据和元数据(在标题中)的单一“数据集”的概念感到满意。对于 XML 数据,数据在一个文件中,而元数据(映射文件)在另一个文件中。
当导出的 SAS 数据仅保留数据 xml 时,元数据会丢失。往返 SAS 意味着必须通过自动映射来猜测元数据。
“已解析的 xml 文档的编程操作”示例代码
此示例将数字列的所有列定义更改为字符。 XPath /SXLEMAP/TABLE/COLUMN[not(@class='ORDINAL') and ./TYPE[text()='numeric'] 用于标识将要更改的列定义。没有进一步的特殊考虑。
创建要处理的xml数据文件
%macro createXmlV2(data=,folder=);
%local lib mem;
%let syslast = &data;
%let lib = %scan(&syslast,1,.);
%let mem = %scan(&syslast,2,.);
FILENAME XMLOUT "&folder./&data..xml";
LIBNAME XMLOUT XMLV2;
proc copy in=&lib out=xmlout;
select &mem;
run;
LIBNAME XMLOUT clear;
FILENAME XMLOUT clear;
%mend;
%* Something to play with;
%* Create an XMLV2 generated xml file containing the data set;
%createXmlV2 (data=sashelp.citiday, folder=/temp)
%createXmlV2 (data=sashelp.citimon, folder=/temp)
%createXmlV2 (data=sashelp.baseball, folder=/temp)
%*;
转换自动映射文件,使数字列变为字符列
%macro prepXmlRefsFor(file=);
FILENAME XMLFILE "&file";
FILENAME MAPOUT "&file..map";
FILENAME MAPOUT2 "&file..map.transformed";
%mend;
%prepXmlRefsFor(file=/temp/sashelp.citiday.xml)
%prepXmlRefsFor(file=/temp/sashelp.baseball.xml)
%prepXmlRefsFor(file=/temp/sashelp.citimon.xml)
%*;
%* create an automap file using XMLV2 library engine;
LIBNAME XMLFILE XMLV2 XMLTYPE=XMLMAP XMLMAP=MAPOUT AUTOMAP=REPLACE ;
%* parse and rewrite the generated map file ;
%* change ALL non-ordinal, non-character COLUMN nodes to indicate character type wanted;
proc groovy;
submit
"%sysfunc(pathname(MAPOUT))"
"%sysfunc(pathname(MAPOUT2))"
"20"
;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
// get parameter from submit line;
map_in=args[0]; // the automap
map_out=args[1]; // the automap transformed
length=args[2]; // length of character value for columns previously considered numeric
// parse mapfile;
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(map_in);
xPath = XPathFactory.newInstance().newXPath();
// select the set of automap nodes that define non-ordinal, numeric columns
columns = xPath.evaluate(
"/SXLEMAP/TABLE/COLUMN" +
"[" +
"not(@class='ORDINAL')" +
" and " +
"./TYPE[text()='numeric']" +
"]", doc, XPathConstants.NODESET);
for (column in columns) {
type = xPath.evaluate("TYPE", column, XPathConstants.NODE);
dtyp = xPath.evaluate("DATATYPE", column, XPathConstants.NODE);
leng = xPath.evaluate("LENGTH", column, XPathConstants.NODE);
type.setTextContent("character");
dtyp.setTextContent("string");
if (leng == null)
column.appendChild(leng = doc.createElement("LENGTH"));
leng.setTextContent(length);
}
// rewrite mapfile with updated nodes
TransformerFactory.newInstance().newTransformer().transform(
new DOMSource(doc), new StreamResult(new File(map_out))
);
println "Programmatic transformation of mapfile completed.";
endsubmit;
quit;
* resubmit libname so libref uses transformed mapfile;
LIBNAME XMLFILE XMLV2 XMLTYPE=XMLMAP XMLMAP=MAPOUT2 AUTOMAP=REUSE;
proc copy in=xmlfile out=work;
run;
LIBNAME XMLFILE clear;
FILENAME XMLFILE clear;
FILENAME MAPOUT clear;
FILENAME MAPOUT2 clear;
在检查“往返”结果后变得显而易见的是,XMLV2创建的 xml 文件在重新读取时将创建 单独列包含缺失值的任何列的命名表。必须合并这些表才能重新创建原始数据集。