php - 如何在 PHP 中解析 OFX(版本 1.0.2)文件?

标签 php xml domdocument sgml ofx

我有一个 OFXCitibank 下载的文件, 这个文件有一个定义在 http://www.ofx.net/DownloadPage/Files/ofx102spec.zip 的 DTD (文件 OFXBANK.DTD),OFX 文件似乎是 SGML有效的。 我正在尝试 DomDocument PHP 5.4.13,但我收到几个警告并且文件未被解析。我的代码是:

$file = "source/ACCT_013.OFX";
$dtd = "source/ofx102spec/OFXBANK.DTD";
$doc = new DomDocument();
$doc->loadHTMLFile($file);
$doc->schemaValidate($dtd);
$dom->validateOnParse = true;

OFX 文件开头为:

OFXHEADER:100
DATA:OFXSGML
VERSION:102
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE

<OFX>
<SIGNONMSGSRSV1>
<SONRS>
<STATUS>
<CODE>0
<SEVERITY>INFO
</STATUS>
<DTSERVER>20130331073401
<LANGUAGE>SPA
</SONRS>
</SIGNONMSGSRSV1>
<BANKMSGSRSV1>
<STMTTRNRS>
<TRNUID>0
<STATUS>
<CODE>0
<SEVERITY>INFO
</STATUS>
<STMTRS>
<CURDEF>COP
<BANKACCTFROM> ...

我愿意在服务器 (Centos) 中安装和使用任何程序以从 PHP 调用。

PD:本类(class) http://www.phpclasses.org/package/5778-PHP-Parse-and-extract-financial-records-from-OFX-files.html不要为我工作。

最佳答案

首先,即使 XML 也是 SGML 的子集,有效的 SGML 文件一定不是格式正确的 XML 文件。 XML 更为严格,并没有使用 SGML 提供的所有功能。

因为 DOMDocument 是基于 XML(而不是 SGML)的,所以这并不真正兼容。

在那个问题旁边,请参阅 Ofexfin1.doc 中的2.2 Open Financial Exchange Headers,它向您解释

The contents of an Open Financial Exchange file consist of a simple set of headers followed by contents defined by that header

进一步:

A blank line follows the last header. Then (for type OFXSGML), the SGML-readable data begins with the <OFX> tag.

所以找到第一个空行并删除所有内容直到那里。然后通过先将 SGML 转换为 XML 将 SGML 部分加载到 DOMDocument 中:

$source = fopen('file.ofx', 'r');
if (!$source) {
    throw new Exception('Unable to open OFX file.');
}

// skip headers of OFX file
$headers = array();
$charsets = array(
    1252 => 'WINDOWS-1251',
);
while(!feof($source)) {
    $line = trim(fgets($source));
    if ($line === '') {
        break;
    }
    list($header, $value) = explode(':', $line, 2);
    $headers[$header] = $value;
}

$buffer = '';

// dead-cheap SGML to XML conversion
// see as well http://www.hanselman.com/blog/PostprocessingAutoClosedSGMLTagsWithTheSGMLReader.aspx
while(!feof($source)) {

    $line = trim(fgets($source));
    if ($line === '') continue;

    $line = iconv($charsets[$headers['CHARSET']], 'UTF-8', $line);
    if (substr($line, -1, 1) !== '>') {
        list($tag) = explode('>', $line, 2);
        $line .= '</' . substr($tag, 1) . '>';
    }
    $buffer .= $line ."\n";
}

// use DOMDocument with non-standard recover mode
$doc = new DOMDocument();
$doc->recover = true;
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
$save = libxml_use_internal_errors(true);
$doc->loadXML($buffer);
libxml_use_internal_errors($save);

echo $doc->saveXML();

此代码示例然后输出以下(重新格式化的)XML,这也表明 DOMDocument 正确加载了数据:

<?xml version="1.0"?>
<OFX>
  <SIGNONMSGSRSV1>
    <SONRS>
      <STATUS>
        <CODE>0</CODE>
        <SEVERITY>INFO</SEVERITY>
      </STATUS>
      <DTSERVER>20130331073401</DTSERVER>
      <LANGUAGE>SPA</LANGUAGE>
    </SONRS>
  </SIGNONMSGSRSV1>
  <BANKMSGSRSV1>
    <STMTTRNRS>
      <TRNUID>0</TRNUID>
      <STATUS>
        <CODE>0</CODE>
        <SEVERITY>INFO</SEVERITY>
      </STATUS>
      <STMTRS><CURDEF>COP</CURDEF><BANKACCTFROM> ...</BANKACCTFROM>
</STMTRS>
    </STMTTRNRS>
  </BANKMSGSRSV1>
</OFX>

我不知道这是否可以根据 DTD 进行验证。也许这行得通。此外,如果 SGML 没有在同一行中使用标记的值编写(并且每行只需要一个元素),那么这种脆弱的转换将会中断。

关于php - 如何在 PHP 中解析 OFX(版本 1.0.2)文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15735330/

相关文章:

javascript - 在 Yii 中使用 RenderPartial 创建模态搜索

sql - 使用 FOR XML PATH 将带有冒号的属性添加到 xml 节点

c# - 如何将XML转换成XPS格式进行打印

javascript - Google map v3 中带有 downloadUrl 的标记 XML 数据

javascript - iframe 相当于窗口吗?

php - Grocery Crud 中两个字段作为一个独特字段

php - MySQL 行数错误

php - Mysql Left Join with Group By 和 Between

php - 按名称获取元素 - HTML DOM 短语

php - 获取兄弟/姐妹节点的值