news 2026/6/5 13:56:09

在PowerBuilder里手写XML序列化——没有现成库的年代怎么拼报文

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在PowerBuilder里手写XML序列化——没有现成库的年代怎么拼报文

在PowerBuilder里手写XML序列化——没有现成库的年代怎么拼报文

PB 9.0原生没有XML序列化,但医保接口的对接需要XML格式的报文。没有库、没有现成的工具——全靠字符串拼接和OLE调用MSXML自己造一套。这篇记录在PB里从零手写XML编解码的过程:转义字符处理、属性拼接、嵌套元素递归、节点解析。

文章目录

  • 在PowerBuilder里手写XML序列化——没有现成库的年代怎么拼报文
    • 一、PB和XML之间的一道鸿沟
    • 二、XML生成——递归构建节点树
    • 三、XML解析——MSXML反向抽取
    • 四、XML生成 vs 解析的策略选择
    • 五、亮点总结
    • 六、适用场景
    • 七、扩展方向
    • 八、结语

一、PB和XML之间的一道鸿沟

医保平台升级后,接口格式从定长报文改成了XML。医院HIS是PB写的,服务端是Java,中间的数据报文要用XML传。

问题来了——PB没有XmlSerializer,没有XDocument,没有JAXB。只有两样东西可以用:字符串拼接OLE调用MSXML

最简单的方案是字符串拼XML,但写了几十条接口后发现——不同地方的字符串拼法不一样,有的没做转义导致&变成非法字符报错,有的嵌套多层全塞在一个方法里改不动。必须抽象出一套通用的工具。


二、XML生成——递归构建节点树

// nv_xml_builder - 自定义类,递归构建XML // 实例变量 string is_xml // 最终XML字符串 boolean ib_indent // 是否格式化缩进 integer ii_level // 当前缩进层级 // constructor constructor: is_xml = '<?xml version="1.0" encoding="UTF-8"?>~r~n' ii_level = 0 end constructor // 设置是否格式化 public subroutine of_setformatted(boolean ab_formatted) ib_indent = ab_formatted end subroutine // 生成缩进 private function of_indent returns string string ls_indent = "" int i if not ib_indent then return "" for i = 1 to ii_level ls_indent += " " next return ls_indent end function // 打开节点(带属性) public subroutine of_open_tag(string as_name, string as_attrs[]) is_xml += of_indent() + "<" + as_name int i for i = 1 to upperbound(as_attrs) step 2 is_xml += " " + as_attrs[i] + '="' + as_attrs[i+1] + '"' next is_xml += ">~r~n" ii_level++ end subroutine // 写入节点值(处理转义) public subroutine of_write_tag(string as_name, string as_value) is_xml += of_indent() + "<" + as_name + ">" is_xml += of_escape(as_value) is_xml += "</" + as_name + ">~r~n" end subroutine // 关闭节点 public subroutine of_close_tag(string as_name) ii_level-- is_xml += of_indent() + "</" + as_name + ">~r~n" end subroutine // 获取最终XML public function of_getxml returns string return is_xml end function // XML转义:& < > " ' private function of_escape(string as_str) returns string as_str = replace(as_str, "&", "&amp;") as_str = replace(as_str, "<", "&lt;") as_str = replace(as_str, ">", "&gt;") as_str = replace(as_str, '"', "&quot;") as_str = replace(as_str, "'", "&apos;") return as_str end function

五个方法覆盖了XML生成的所有场景:开标签、写值、关标签、属性、转义。调用时按XML结构逐级嵌套:

nv_xml_builder lxml lxml = create nv_xml_builder string ls_attrs[] ls_attrs[1] = "infno" ls_attrs[2] = "1101" ls_attrs[3] = "msgid" ls_attrs[4] = "20260525120001" lxml.of_open_tag("TCS101Message", ls_attrs) ls_attrs[1] = "" // 子节点无属性 ls_attrs[2] = "" lxml.of_open_tag("MessageHead", ls_attrs) lxml.of_write_tag("SenderId", "T46100009000") lxml.of_write_tag("ReceiverId", "T46100008000") lxml.of_write_tag("SendTime", "20260525120001") lxml.of_close_tag("MessageHead") lxml.of_open_tag("MessageBody", ls_attrs) // 递归写入业务数据 lxml.of_write_nodefromdw(dw_person, "personInfo", ...) lxml.of_close_tag("MessageBody") lxml.of_close_tag("TCS101Message") string ls_xml = lxml.of_getxml()

生成的XML:

<?xml version="1.0" encoding="UTF-8"?><TCS101Messageinfno="1101"msgid="20260525120001"><MessageHead><SenderId>T46100009000</SenderId><ReceiverId>T46100008000</ReceiverId><SendTime>20260525120001</SendTime></MessageHead><MessageBody>...</MessageBody></TCS101Message>

三、XML解析——MSXML反向抽取

XML的生成可以用字符串拼,但解析不行——字符串split对付不了嵌套。PB的做法是调用Windows内置的MSXML组件:

// nv_xml_reader - 基于OLE MSXML的XML解析器 OLEObject iole_xml constructor: iole_xml = create OLEObject iole_xml.ConnectToNewObject("Msxml2.DOMDocument.4.0") end constructor // 加载XML字符串 public subroutine of_loadxml(string as_xml) ole_xml.async = false try ole_xml.loadxml(as_xml) if ole_xml.parseerror.errorcode <> 0 then messagebox("解析错误", ole_xml.parseerror.reason) end if catch (Exception e) messagebox("加载失败", e.getmessage()) end try end subroutine // 读取节点值 public function of_getelement(string as_path) returns string OLEObject lnode try lnode = ole_xml.selectSingleNode(as_path) if not isnull(lnode) then return lnode.text end if catch (Exception e) end try return "" end function // 读取属性 public function of_getattribute(string as_path, string as_attr) returns string OLEObject lnode try lnode = ole_xml.selectSingleNode(as_path) if not isnull(lnode) then return lnode.getAttribute(as_attr) end if catch (Exception e) end try return "" end function // 读取节点列表 public function of_getnodes(string as_path, ref OLEObject a_nodes[]) returns int OLEObject lnodelist, lnode int li_count = 0, i try lnodelist = ole_xml.selectnodes(as_path) for i = 0 to lnodelist.length - 1 li_count++ a_nodes[li_count] = lnodelist.item(i) next catch (Exception e) end try return li_count end function

解析医保接口返回的XML报文:

nv_xml_reader lreader lreader = create nv_xml_reader // 接口返回的XML lreader.of_loadxml(as_response) // 读公共头 string ls_code = lreader.of_getelement("//output/result/code") string ls_msg = lreader.of_getelement("//output/result/message") // 读业务数据——人员列表 OLEObject a_nodes[] int li_count li_count = lreader.of_getnodes("//output/personList/personInfo", a_nodes) int i for i = 1 to li_count string ls_name = a_nodes[i].selectSingleNode("aac003").text string ls_idcard = a_nodes[i].selectSingleNode("aac002").text // 逐条入库... next

XPath表达式//output/personList/personInfo直接定位到列表节点,selectSingleNode取子节点值。MSXML的优势是有完整的XPath和DOM支持,比用字符串split硬解析强悍太多。


四、XML生成 vs 解析的策略选择

场景方案原因
生成XML发给对方递归类拼字符串性能好,纯PB代码无外部依赖
解析对方返回的XMLOLE MSXMLXPath查询方便,DOM操作比字符串split稳健
内部系统间交换递归类双向处理不用OLE,部署简单

生成用字符串拼是因为性能——一次请求可能生成几十个节点的XML,纯字符串拼接比OLE快。解析用MSXML是因为稳健——对方返回的XML嵌套深度不确定,XPath+DOM比手写字符串解析靠谱得多。


五、亮点总结

✅ XML生成纯PB代码——字符串递归拼装,零外部依赖
✅ XML解析借力MSXML——XPath+DOM,支持复杂嵌套查询
✅ 五字符转义全处理——& < > " '五个XML特殊字符逐个替换
✅ 双模式策略——生成用字符串(快),解析用MSXML(稳)
✅ 完整调用链——从PB DataWindow到XML报文到接口对接,全链路覆盖

六、适用场景

  • PB老系统需要对接XML格式的外部接口(医保平台、银行接口、政务平台)
  • 字符串拼接生成XML——适合输出报文给对方
  • MSXML解析XML——适合处理对方返回的复杂嵌套响应

七、扩展方向

  1. DTD/Schema校验——加载XML前先用MSXML验证结构合法性
  2. CDATA支持——处理包含特殊字符的长文本字段
  3. 流式解析——超大XML文件用SAX解析替代DOM,避免内存撑爆
  4. 命名空间支持——处理带xmlns:前缀的XML报文

八、结语

在PB生态里造XML工具——这个选择本身就是一个权衡。字符串拼接快但转义容易漏,MSXML功能全但部署有依赖。2014年的选择是两套并存——生成用自制类(快)、解析用MSXML(稳)。放到今天,所有的现代语言都自带了XML序列化,但在那个PB主导的年代,这套工具就是医院HIS和医保平台之间所有数据交换的管道。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/5 13:54:32

QueryExcel:如何3分钟内完成100个Excel文件的内容搜索?

QueryExcel&#xff1a;如何3分钟内完成100个Excel文件的内容搜索&#xff1f; 【免费下载链接】QueryExcel 多Excel文件内容查询工具。 项目地址: https://gitcode.com/gh_mirrors/qu/QueryExcel 在日常办公场景中&#xff0c;Excel文件内容搜索已成为数据工作者面临的…

作者头像 李华
网站建设 2026/6/5 13:54:28

千问 LeetCode 2959. 关闭分部的可行集合数目 Java实现

这道题是一道经典的状态压缩&#xff08;二进制枚举&#xff09; 图论&#xff08;最短路&#xff09;的问题。&#x1f4a1; 核心解题思路1. 状态压缩&#xff08;二进制枚举&#xff09;&#xff1a;题目中分部的数量 n 最大为 10&#xff0c;这是一个非常小的数据规模。我们…

作者头像 李华
网站建设 2026/6/5 13:50:12

Spring AI 生产级实战:向量数据库

一、为什么需要向量数据库&#xff1f; 在前面的 Spring AI 生产级实战中&#xff0c;我们已经讲到了 RAG&#xff0c;也就是检索增强生成。 RAG 的核心思路是&#xff1a; 先检索相关资料&#xff0c;再让大模型基于资料回答。但这里有一个关键问题&#xff1a;用户提出的问题…

作者头像 李华
网站建设 2026/6/5 13:47:02

从现象到根因:智能仓储物流电控柜与外围设备PLC故障拆解

作者&#xff1a;宽海智能仓储物流制造业智能仓储物流集成专家-宽海智能软硬一体化解决方案&#xff1a;维修保养-升级改造-烂尾盘活-项目新建WMS-WCS-PLC-AGV-CTU-堆垛机-输送设备-穿梭车-机器人-SCADA-数字孪生-TMS-MES引言在智能仓储物流领域&#xff0c;我们常说“软件定义…

作者头像 李华
网站建设 2026/6/5 13:43:57

Window Resizer终极指南:免费开源工具帮你掌控任意窗口大小

Window Resizer终极指南&#xff1a;免费开源工具帮你掌控任意窗口大小 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer 你是否曾被某些顽固的应用程序窗口困扰过&#xff1f;那些无…

作者头像 李华