diff --git a/domain_resource/src/main/ets/resource/VersionXml.ets b/domain_resource/src/main/ets/resource/VersionXml.ets
index 5d9c916..32e2839 100644
--- a/domain_resource/src/main/ets/resource/VersionXml.ets
+++ b/domain_resource/src/main/ets/resource/VersionXml.ets
@@ -1,15 +1,15 @@
/**
- * version.xml 解析(契约 §5.3,框架 §8.3)。用 @kit.ArkTS xml.XmlPullParser。
+ * version.xml 解析(契约 §5.3,框架 §8.3)。
*
* 结构:
* 用途:取 与远程 game_version 比较决定是否更新;取子
- * 作 app_data 的 app_gamename。`value` 为 version 标签独有;`name` 多标签共有,故按
- * 所属标签(当前为子 game)区分抓取。
+ * 作 app_data 的 app_gamename;取子 作大厅 gameid 为空时的回退匹配 key。
+ *
+ * 实现说明:早期用 @kit.ArkTS xml.XmlPullParser,实测其 attributeValueCallbackFunction 对
+ * 本文件 `` 的 `value` 属性**不稳定触发**(同文件 `` 却能取到),
+ * 导致 version 恒为 0、子游戏每次都误判需更新而重复下载。改用确定性正则直接从原文抽属性,
+ * 语义等价于 Android `pullxml1`,规避解析器回调时序/属性触发的不确定性。
*/
-import { xml, util } from '@kit.ArkTS';
-import { Logger } from 'common';
-
-/** version.xml 解析结果。 */
export interface VersionXmlInfo {
/** 整数(缺失/失败 → 0)。 */
version: number;
@@ -20,46 +20,36 @@ export interface VersionXmlInfo {
}
export class VersionXml {
- private static readonly log: Logger = Logger.tag('VersionXml');
+ // :取版本号。允许属性间任意顺序/空白。
+ private static readonly VERSION_RE: RegExp = new RegExp(']*\\bvalue\\s*=\\s*"([^"]*)"', 'i');
+ // 子 ...>:根 无属性(其后紧跟 '>'),故 \s+ 自然跳过,只命中带属性的子 game。
+ private static readonly GAME_TAG_RE: RegExp = new RegExp(']*?>', 'i');
+ private static readonly ID_RE: RegExp = new RegExp('\\bid\\s*=\\s*"([^"]*)"', 'i');
+ private static readonly NAME_RE: RegExp = new RegExp('\\bname\\s*=\\s*"([^"]*)"', 'i');
- /** 解析 version + gameName。属性回调紧随其开始标签,故用 currentTag 把 name 归属到 game 标签。 */
+ /** 解析 version + gameName + gameId(任一缺失取默认值,绝不抛错)。 */
static parse(xmlText: string): VersionXmlInfo {
const info: VersionXmlInfo = { version: 0, gameName: '', gameId: '' };
if (xmlText.length === 0) {
return info;
}
- let value: string = '';
- let currentTag: string = '';
- try {
- const buf: Uint8Array = new util.TextEncoder().encodeInto(xmlText);
- const parser: xml.XmlPullParser = new xml.XmlPullParser(buf.buffer as object as ArrayBuffer, 'UTF-8');
- const options: xml.ParseOptions = {
- ignoreNameSpace: true,
- tagValueCallbackFunction: (name: string, _val: string): boolean => {
- currentTag = name;
- return true;
- },
- attributeValueCallbackFunction: (name: string, val: string): boolean => {
- if (name === 'value') {
- value = val;
- } else if (name === 'name' && currentTag === 'game') {
- // 根 无 name 属性;只有子 触发,正是所需游戏名
- info.gameName = val;
- } else if (name === 'id' && currentTag === 'game') {
- // 子 的 id:大厅 gameid 回退匹配 key(根 无 id 属性)
- info.gameId = val;
- }
- return true;
- },
- };
- parser.parseXml(options);
- } catch (e) {
- const err = e as Error;
- VersionXml.log.w(`parse version.xml failed: ${err.message}`);
- return info;
+ const verMatch: RegExpMatchArray | null = xmlText.match(VersionXml.VERSION_RE);
+ if (verMatch !== null) {
+ const n: number = parseInt(verMatch[1], 10);
+ info.version = isNaN(n) ? 0 : n;
+ }
+ const gameMatch: RegExpMatchArray | null = xmlText.match(VersionXml.GAME_TAG_RE);
+ if (gameMatch !== null) {
+ const tag: string = gameMatch[0];
+ const idMatch: RegExpMatchArray | null = tag.match(VersionXml.ID_RE);
+ if (idMatch !== null) {
+ info.gameId = idMatch[1];
+ }
+ const nameMatch: RegExpMatchArray | null = tag.match(VersionXml.NAME_RE);
+ if (nameMatch !== null) {
+ info.gameName = nameMatch[1];
+ }
}
- const n: number = parseInt(value, 10);
- info.version = isNaN(n) ? 0 : n;
return info;
}