IBM i SAX XML 解析约 889570 条记录后无报错停止,如何调试?
结论
目前证据不足,无法将“约 889,570 条记录后停止”认定为固定记录数限制或 IBM i 7.3 的已知缺陷。应先补齐错误反馈,再排查文件大小、资源消耗或特定数据是否触发了问题。
先检查两处:
- 旧 Qxml 封装通过
Qxml_DOMExcData反馈部分高层错误,只注册 SAX ErrorHandler 可能漏掉失败原因。 - 按经典接口的定义,
QxmlSAXParser_setErrorHandler()接收 ErrorHandler 对象指针,需要核对示例中直接传入%paddr(MyErrorHandler)的写法。
QxmlSAXParser_parse_systemid() 属于旧 alphaWorks XML 封装,与 RPG 内置 XML-SAX 是两套接口,错误处理方式和大小限制不能混用。旧 Qxml 接口及示例说明
先确认“停止”发生在哪一步
在解析调用前后记录日志,并在 document handler 中记录文档结束事件。至少保留以下信息:
- 调用解析器前的时间、文件路径和实际字节数。
- 最后完成的业务记录编号和记录主键。
- 是否收到文档结束事件。
QxmlSAXParser_parse_systemid()是否返回。- 返回后立即读取的错误反馈。
| 观察结果 | 优先检查方向 |
|---|---|
| 解析调用没有返回,作业仍在运行 | 查看调用栈,确认是否等待数据库、文件 I/O 或其他资源 |
| 解析调用返回,但没有文档结束事件 | 检查封装反馈区、回调异常和提前退出路径 |
| 收到文档结束事件,但输出记录不足 | 核对实际输入文件、业务过滤条件、数据库写入及计数逻辑 |
| 作业已经结束,调用后的日志未写入 | 检查完整 joblog、调用方和作业结束原因 |
“输出表只有 889,570 条”不能直接说明“解析器只读取了 889,570 条”。建议分别统计业务元素完成数和成功写入数,将计数位置放在对应业务元素的结束事件中。
补齐旧 Qxml 接口的错误处理
检查 Qxml_DOMExcData
先找到程序实际使用的 /COPY 成员及其绑定的服务程序,核对接口版本。
初始化这套封装时,需要将异常反馈区传给 QxmlInit()。部分 SAX 高层错误会写入 Qxml_DOMExcData,应重点检查其中的 Qxml_DOMRtnCod。具体结构、初始化参数和返回码含义,以本机配套头文件为准。
创建解析器、创建 handler、注册回调和解析完成后,都应检查反馈。发现失败时,先保存完整反馈区,再做清理,避免后续调用覆盖现场。
以下诊断片段可插入现有程序,前提是原型和反馈区已正确声明、初始化:
dcl-s savedDomRc like(Qxml_DOMRtnCod);
dcl-s rpgStatus int(10) inz(0);
dcl-s parseReturned ind inz(*off);
monitor;
QxmlSAXParser_parse_systemid(saxParser: xmlfile: 37: 0);
// 在调用其他 Qxml 方法之前保存反馈
savedDomRc = Qxml_DOMRtnCod;
parseReturned = *on;
on-error;
rpgStatus = %status();
// 在这里持久化 RPG 状态、PSDS 和业务进度
endmon;
两条错误通道需要分别检查:
MONITOR / ON-ERROR捕获能够传播到该位置的 RPG 异常。Qxml_DOMRtnCod反映封装自身报告的结果。
如果封装在内部捕获异常后只写入反馈区,外层 ON-ERROR 就不会被触发。因此,调用正常返回也不能证明解析成功。
核对 ErrorHandler 对象与回调的注册方式
如果本机使用经典 Qxml 封装,注册方式应类似下面的代码。原型、常量和回调参数必须使用同一版本的头文件:
errorHandler = QxmlErrorHandler_new();
QxmlErrorHandler_setCallback(
errorHandler: Qxml_WARNINGHNDLR: %paddr(MyWarning));
QxmlErrorHandler_setCallback(
errorHandler: Qxml_ERRORHNDLR: %paddr(MyError));
QxmlErrorHandler_setCallback(
errorHandler: Qxml_FATALERRORHNDLR: %paddr(MyFatalError));
QxmlSAXParser_setErrorHandler(saxParser: errorHandler);
代码省略了逐次检查和清理部分,实际程序中每一步都应检查反馈结果。
如果问题中的代码只是简写,实际程序已经创建 ErrorHandler 对象,可以继续排查其他方向;如果确实直接传入了过程地址,应先修正这一处。小文件运行成功,仍不足以证明错误回调路径正确。
还应做一次受控测试:在小型测试 XML 中故意制造标签不匹配,确认错误回调能记录消息,主程序也能识别失败。测试时保留原始大文件不变。
如何判断是否与文件大小有关
记录数和文本行数都不能代表解析器实际处理的数据量。应记录文件字节数,并比较不同输入的停止位置。
IBM 对 RPG 内置 XML 解析器公布的限制包括:
- 单字节 CCSID:最多处理
2147483408个字符。 - UCS-2:最多处理
1073741704个 UCS-2 字符。
这些数值适用于 RPG 内置 XML 解析器,不能直接作为旧 QxmlSAXParser_parse_systemid() 的限制,也不能简化为“UCS-2 文件超过 1 GB 就会失败”。IBM:XML 解析器限制
可以对当前程序做以下对照实验:
- 保留业务记录,缩短非关键文本,观察停止时的记录数是否改变。
- 将停止位置附近的记录组成一个独立、格式完整的小 XML 文件。
- 暂时移除回调中的数据库写入、字符串转换和业务处理,只保留计数。
根据结果缩小排查范围:
- 如果总在同一条业务数据附近失败,优先检查该数据和回调逻辑。
- 如果不同输入都在相近处理量附近失败,优先检查资源累计或内部容量边界。
- 如果纯计数可以完成,恢复业务处理后失败,重点排查回调中的操作。
拆分测试文件时,要保留根元素、命名空间和必要声明,不能直接按行截断 XML。
编码参数不要盲目改成 819
需要分别核对以下几项:
- XML 文件的实际字节编码。
- XML 声明中的
encoding。 - IFS 文件的 CCSID 属性。
- 传给 API 的路径字符串编码。
按经典 Qxml 封装的参数定义,下面调用中的 37 指定的是 xmlfile 所指向的文件名字符串的 CCSID:
QxmlSAXParser_parse_systemid(saxParser: xmlfile: 37: 0);
文件内容使用 ISO-8859-1,并不意味着这里必须改成 819。第四个参数也应使用本机原型规定的类型;经典调用用数值 0 表示文件名字符串以空字符结尾。Qxml 文件名参数说明
修改 XML 声明或 IFS CCSID 标签,并不会转换文件字节。做转换实验时,应保存原始文件,并记录转换前后的编码、字节数和解析结果。
进一步收集作业和内存证据
在实际执行解析的作业中提高日志级别,重现问题后打印 joblog:
CHGJOB JOB(*) LOG(4 00 *SECLVL) LOGCLPGM(*YES)
DSPJOBLOG JOB(*) OUTPUT(*PRINT)
批处理场景需要指定目标作业,不能只修改当前交互作业。这些命令可以收集更完整的作业消息,但不会自动将封装反馈区写入日志,反馈区仍需由程序记录。IBM 作业日志收集示例
使用带调试信息的构建,在接近停止位置时观察最后一个回调,以及解析调用能否返回。如果作业仍然存在,先查看调用栈,确认它正在执行什么。
检查内存时,应关注处理过程中是否有持续累积的情况:
- 每条记录创建的转换字符串或临时对象是否释放。
- 数组、缓存、日志缓冲区是否持续增长。
- 是否将解析器提供的临时指针保存到后续回调中使用。
- 数据库错误是否被回调中的错误处理吞掉。
一次 DUMP(A) 未发现异常,只能说明当时没有观察到明显问题,仍不能排除逐条泄漏或更早发生的内存破坏。
ACTGRP(*CALLER) 本身不足以解释这个停止点。可以在新的测试作业中比较首次运行和重复运行的行为,同时核对初始化与清理是否配对。
需要重写时,可考虑 RPG 内置 XML-SAX
内置 XML-SAX 有另一套明确的错误接口。以下调用片段假定 XMLHandler 和通信区已经按规定声明:
xml-sax(e) %handler(XMLHandler: commArea)
%xml(ifsPath: 'doc=file');
if %error();
rpgStatus = %status();
// 保存状态及通信区中的异常信息,向调用方报告失败
endif;
错误处理需要同时覆盖:
XML-SAX(E)后的%ERROR()和%STATUS()。- handler 收到的
*XML_EXCEPTION。
IBM 文档列出的相关状态包括 00351、00352、00354。如果在解析开始前失败,例如无法打开文件,handler 可能根本收不到事件,因此必须保留调用后的检查。IBM:XML-SAX 错误处理
处理 *XML_EXCEPTION 时,保存 exceptionId 和长度参数。该事件的数据指针参数无效,不要读取。调试内置 handler 时可使用 DEBUG(*XMLSAX)。IBM:XML 事件及调试说明
迁移时仍需核对字符转换、实体处理和业务回调行为,并用原始大文件验收。旧程序应先补上解析返回后保存 Qxml_DOMExcData 的处理。完成检查则需同时满足:收到文档结束事件、无错误反馈、业务数量符合预期。
备注:内容仅供参考。
