Изменение типа данных в файле карты SAS XML - PullRequest
0 голосов
/ 19 марта 2020

Мне нужно контролировать тип данных при чтении данных XML в SAS. Данные XML записываются и обрабатываются с использованием механизма XML libname в SAS.

XML Файл:

<Test>
   <origin>YYYY</origin>
   <NumToUse>50503</NumToUse>
   <AcctNum>3-219HHJLJ</AcctNum>
   <Status>1</Status>
   <TADIG>AUSVF</TADIG>
   <LocationNumber>1234567891011</LocationNumber>
   <Phnumber>1234567890</Phnumber>
   <ReferenceNumber>0044E71146</ReferenceNumber>

Файл карты:

    <COLUMN name="LocationNumber">
        <PATH syntax="XPath">/Test/LocationNumber</PATH>
        <TYPE>character</TYPE>
        <DATATYPE>string</DATATYPE>
        <LENGTH>11</LENGTH>
    </COLUMN>

    <COLUMN name="PhNumber">
        <PATH syntax="XPath">/Test/PhNumber</PATH>
        <TYPE>character</TYPE>
        <DATATYPE>string</DATATYPE>
        <LENGTH>15</LENGTH>
    </COLUMN>

    <COLUMN name="ReferenceNumber">
        <PATH syntax="XPath">/Test/ReferenceNumber</PATH>
        <TYPE>numeric</TYPE>
        <DATATYPE>double</DATATYPE>
    </COLUMN>

Поскольку ссылочный номер обрабатывается как Numeri c, я не могу получить значение для этого конкретного столбца. Это дает мне

ERROR: Data contains invalid content for float datatype. Invalid content is 0044E71146

Как прочитать данные в наборе данных SAS? предложение пожалуйста

Ответы [ 3 ]

1 голос
/ 22 марта 2020

Файл карты, созданный библиотекой XMLV2, можно изменить перед использованием libref для копирования данных в сеанс SAS.

Существует много способов обработки файла карты, сгенерированного движком (который является xml сам файл)

  • XSL-преобразование (Pro c XSL)
    • Кто-то (не я), хорошо разбирающийся в языке XSLT, может написать короткую программу для выполнения модификаций
  • Programmati c манипулирование разобранным xml документом
  • Текстовое манипулирование xml файлом
    • Ручное редактирование
    • Программа обработки текста

Файл карты :

  • определяет, какие xml узлы должны стать столбцами набора данных
    • определяет тип, длину, формат, метку и т. Д. c столбца
  • определяет, какие таблицы должны быть созданы
  • определяет какие таблицы должны содержать какие столбцы

В случае необходимости изменить Интерпретация механизма XMLV2 числовых c столбцов в символьные столбцы, файл карты необходимо изменить (он же преобразованный ).

  • <COLUMN> узлы имеют дочерние узлы, которые на минимум, необходимо изменить
    с
    <TYPE>numeric</TYPE>
    на
    <TYPE>character</TYPE>

Соображения

Есть другие соображения, которые вам, возможно, придется учитывать при преобразовании чисел в набор символов, например:

  • Является ли эта цифра c датой?
  • Должно ли число отображаться сначала в его формате? ?

Пользователи SAS довольны концепцией единого «набора данных», который содержит как данные, так и метаданные (в заголовке). Для данных XML данные находятся в одном файле, а метаданные (файл карты) - в другом.

Когда экспортируются данные SAS и сохраняются только данные xml, метаданные теряются. Возврат в обратном направлении в SAS означает, что метаданные должны быть угаданы через autop.

Пример кода для "Programmati c манипулирования разобранным xml документом"

В этом примере все определения столбцов для числовых значений c заменяются символами. 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)

%*;

Преобразование файла автомата таким образом, чтобы числовые столбцы c стали столбцы символов

%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;

Одна вещь, которая стала очевидной после изучения результата «туда-обратно», состоит в том, что xml файлы созданы с помощью XMLV2 при повторном чтении , создаст отдельные таблицы с именами столбцов для любых столбцов, которые содержат пропущенные значения. Эти таблицы должны быть объединены для воссоздания исходного набора данных.

1 голос
/ 20 марта 2020

Возможно, вы понимаете, что функция автоматического отображения, встроенная в движок XMLV2, выбирает определение ReferenceNumber как числа, а не как символа, потому что единственное значение, которое анализатор анализирует, это 0044E71146 и предполагает #E# - это научная c (или экспоненциальная) запись для числа.

Решение состоит в том, чтобы позволить libname автоматизировать файл данных xml, а затем обновить файл карты xml в соответствии с вашими требованиями.

Пример кода:

XMLV2 движок создает MAPFILE, а Proc GROOVY используется для XML анализа и перезаписи файла карты.

FILENAME XMLFILE "/temp/test.xml" ;
FILENAME MAPFILE "/temp/test.xml.map" ;

* parse data test.xml and write mapfile test.xml.map;
LIBNAME XMLFILE XMLV2 XMLTYPE=XMLMAP XMLMAP=MAPFILE AUTOMAP=REPLACE ;

* parse and rewrite mapfile;
* change desired column nodes to be string/character of a specified length;

proc groovy;
submit "%sysfunc(pathname(mapfile))";
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

* get parameter from submit line;

mapfile=args[0];

* parse mapfile;

doc = DocumentBuilderFactory
      .newInstance()
      .newDocumentBuilder()
      .parse(
        mapfile
      )
      ;

xPath = XPathFactory
        .newInstance()
        .newXPath()
        ;

void setCharacter(column,length) {

  * find column node and child nodes important to XMLV2 mapfile usage;

  node = xPath.evaluate("/SXLEMAP/TABLE/COLUMN[@name='"+column+"']", doc, XPathConstants.NODE);
  type = xPath.evaluate("TYPE",     node, XPathConstants.NODE);
  dtyp = xPath.evaluate("DATATYPE", node, XPathConstants.NODE);
  leng = xPath.evaluate("LENGTH",   node, XPathConstants.NODE);

  if (type != null && !type.getTextContent().equals("character")) { type.setTextContent("character") } 
  if (dtyp != null && !dtyp.getTextContent().equals("string"))    { dtyp.setTextContent("string") } 
  if (leng == null) {
    leng = doc.createElement("LENGTH");
    leng.setTextContent(length.toString());
    node.appendChild(leng);
  }
  else
  if (!length.getTextContent().equals(length.toString())) {
    leng.setTextContent(length.toString());
  }
}

// Make sure these two columns will be character, if not already

setCharacter("ReferenceNumber",25);
setCharacter("Phnumber", 20);

// rewrite mapfile with updated nodes

TransformerFactory
.newInstance()
.newTransformer()
.transform(
  new DOMSource(doc),
  new StreamResult(new File(mapfile))
);

endsubmit;
quit;

* resubmit libname so libref uses now updated mapfile;

LIBNAME XMLFILE XMLV2 XMLTYPE=XMLMAP XMLMAP=MAPFILE;

proc copy in=xmlfile out=work;
run;

Примечание : Вы можете анализировать и переписывать файл карты в текстовом виде, однако существует небольшая внешняя вероятность того, что файл карты может не соответствовать вашим ожиданиям при «разборе текста».

0 голосов
/ 20 марта 2020

Пусть SAS создаст файл карты.

FILENAME XMLFILE "/v/temp/test.xml" ; 
FILENAME MAPFILE "/v/temp/test.xml.map" ; 
LIBNAME XMLFILE XMLV2 XMLTYPE=XMLMAP XMLMAP=MAPFILE AUTOMAP=REUSE ; 

Отредактируйте файл и исправьте определение

<COLUMN name="ReferenceNumber">
    <PATH syntax="XPath">/Test/ReferenceNumber</PATH>
    <TYPE>character</TYPE>
    <DATATYPE>string</DATATYPE>
    <LENGTH>15</LENGTH>
</COLUMN>

Возможно, вы захотите сохранить его в постоянном, а не во временном местоположении. Теперь используйте исправленный файл, чтобы переопределить libref.

FILENAME XMLFILE "/v/temp/test.xml" ; 
FILENAME MAPFILE "/v/permanent/fixed_xml.map" ; 
LIBNAME XMLFILE XMLV2 XMLTYPE=XMLMAP XMLMAP=MAPFILE AUTOMAP=REUSE ; 
...