关于3.1 Release中@ohos.xml无法解析XML多个属性问题分析报告

Winslei丶 2023-03-13 18:51:30

1 关键字

xml;parse;

2 问题描述

开发板型号:

芯片:

内核版本:

OH版本:OpenHarmony 3.1 Release

问题现象:OpenHarmony 3.1 Release系统上使用@ohos.xml中的xmlPullParser.parse接口解析XML文件获取Tag属性值,同个Tag只能获取到第一个属性值,无法获取其之后的属性值。

测试步骤:

  1. 创建应用并编写以下代码:
import xml from '@ohos.xml';
var strXml =
'<?xml version="1.0" encoding="utf-8"?>' +
'<!DOCTYPE note [\n<!ENTITY foo "baa">]>' +
'<note importance="high" logged="true">' +
' <![CDATA[\r\nfuncrion matchwo(a,6)\r\n{\r\nreturn 1;\r\n}\r\n]]>' +
' <!--Hello, World!-->' +
' <company>John &amp; Hans</company>' +
' <title>Happy</title>' +
' <title>Happy</title>' +
' <lens>Work</lens>' +
' <lens>Play</lens>' +
' <?go there?>' +
' <a><b/></a>' +
' <h:table xmlns:h="http://www.w3.org/TR/html4/">' +
' <h:tr>' +
' <h:td>Apples</h:td>' +
' <h:td>Bananas</h:td>' +
' </h:tr>' +
' </h:table>' +
'</note>';
var arrayBuffer = new ArrayBuffer(strXml.length*2);
var bufView = new Uint8Array(arrayBuffer);
var strLen = strXml.length;
for (var i = 0; i < strLen; ++i) {
bufView[i] = strXml.charCodeAt(i);
}
var that = new xml.XmlPullParser(arrayBuffer, 'UTF-8');
var str = '';
function func2(name, value){
str += name+':'+value;
return true;
}
var options = {supportDoctype:true, ignoreNameSpace:true, attributeValueCallbackFunction:func2}
that.parse(options);
console.log('xml str value:' + str);
  1. 运行应用打印结果

3 问题原因

3.1 正常机制

3.2 异常机制

4 解决方案

通过下面的方式,可以解决此问题,在源代码base/compileruntime/js_api_module/xml/js_xml.cpp的 XmlPullParser::ParseAttri 解析xml属性的方法中,改动for循环中最后一行代码:

bool XmlPullParser::ParseAttri(napi_env env, napi_value thisVar) const
{
    for (size_t i = 0; i < attriCount_; ++i) {
        napi_value returnVal = nullptr;
        size_t argc = 3; // 3: number of args
        napi_value global = nullptr;
        napi_get_global(env, &global);
        napi_value key = nullptr;
        napi_create_string_utf8(env, attributes[i * 4 + 2].c_str(), // 4 and 2: number of args
            attributes[i * 4 + 2].size(), &key); // 4 and 2: number of args
        napi_value value = nullptr;
        napi_create_string_utf8(env, attributes[i * 4 + 3].c_str(), // 4 and 3: number of args
            attributes[i * 4 + 3].size(), &value); // 3 and 4: number of args
        napi_value argv[3] = {key, value, thisVar};
        napi_call_function(env, global, attrFunc_, argc, argv, &returnVal);
        bool bRec = false;
        napi_get_value_bool(env, returnVal, &bRec);
        // return bRec; //将此行代码注释
        // 添加以下代码即可
        if (!bRec) {
            return bRec;
        }
    }
    return true;
}

5 定位过程

通过对比OpenHarmony 3.1 Release和master分支编译的系统发现,在master分支中解析xml标签属性是正常的,3.1release只会解析标签上的第一个属性,找到源码中(base/compileruntime/js_api_module/xml/js_xml.cpp)关于xml解析的代码进行对比发现有以下的不同:

  1. XmlPullParser::ParseAttri
bool XmlPullParser::ParseAttri(napi_env env, napi_value thisVar) const
{
    for (size_t i = 0; i < attriCount_; ++i) {
        napi_value returnVal = nullptr;
        size_t argc = 3; // 3: number of args
        napi_value global = nullptr;
        napi_get_global(env, &global);
        napi_value key = nullptr;
        napi_create_string_utf8(env, attributes[i * 4 + 2].c_str(), // 4 and 2: number of args
            attributes[i * 4 + 2].size(), &key); // 4 and 2: number of args
        napi_value value = nullptr;
        napi_create_string_utf8(env, attributes[i * 4 + 3].c_str(), // 4 and 3: number of args
            attributes[i * 4 + 3].size(), &value); // 3 and 4: number of args
        napi_value argv[3] = {key, value, thisVar};
        napi_call_function(env, global, attrFunc_, argc, argv, &returnVal);
        bool bRec = false;
        napi_get_value_bool(env, returnVal, &bRec);
        // 3.1release 直接返回bRec
        //return bRec;
        
        // master 判断后返回
        if (!bRec) {
            return bRec;
        }
    }
    return true;
}
  1. XmlPullParser::Parse
void XmlPullParser::Parse(napi_env env, napi_value thisVar)
{
    if (tagFunc_ || attrFunc_ || tokenFunc_) {
        while (type != TagEnum::END_DOCUMENT) {
            ParseOneTag();
            bool bRec = false;
            if (tagFunc_ && type == TagEnum::START_TAG) {
                napi_value returnVal = nullptr;
                size_t argc = 3; // 3: number of args
                napi_value global = nullptr;
                napi_get_global(env, &global);
                napi_value key = nullptr;
                napi_create_string_utf8(env, name_.c_str(), name_.size(), &key);
                napi_value value = nullptr;
                napi_create_string_utf8(env, text_.c_str(), text_.size(), &value);
                napi_value argv[3] = {key, value, thisVar};
                napi_call_function(env, global, tagFunc_, argc, argv, &returnVal);
                napi_get_value_bool(env, returnVal, &bRec);
            }
            if (tagFunc_ && type == TagEnum::START_TAG && !bRec) {
                break;
            }
            if (attrFunc_ && attriCount_) {
                bRec = ParseAttri(env, thisVar);
                // 3.1release中无下一行代码,master中存在
                attriCount_ = 0;
            }
            if (attrFunc_ && attriCount_ && !bRec) {
                break;
            }
            if (tokenFunc_) {
                bRec = ParseToken(env, thisVar);
            }
            if (tokenFunc_ && !bRec) {
                break;
            }
        }
    }
}
  1. XmlPullParser::ParseTagValueFunc的入参写法不一致
// 3.1release
bool XmlPullParser::ParseTagValueFunc(char c, bool bFlag, TextEnum textEnum, size_t &start, std::string& result)

// master
bool XmlPullParser::ParseTagValueFunc(char &c, bool bFlag, TextEnum textEnum, size_t &start, std::string &result)
  1. XmlPullParser::ParseTagValue的方法体中变量写法有差异
// 3.1release
std::string XmlPullParser::ParseTagValue(char delimiter, bool resolveEntities,
    bool throwOnResolveFailure, TextEnum textEnum)
{
    size_t start = position_;
    std::string result = " ";
    if (textEnum == TextEnum::TEXT && text_ != "") {
        result.append(text_);
    }
    while (true) {
        size_t iRecv = ParseTagValueInner(start, result, delimiter, textEnum, resolveEntities);
        if (iRecv == 0) {
            return result;
        } else if (iRecv == 1) {
            break;
        } else if (iRecv == 2) { // 2: break flag
            continue;
        } else if (!ParseTagValueFunc(static_cast<char>(iRecv), throwOnResolveFailure, textEnum, start, result)) {
            continue;
        }
        ++position_;
        result = result + static_cast<char>(iRecv);
        start = position_;
    }
    result.append(strXml_, start, position_ - start);
    return result;
}

// master
std::string XmlPullParser::ParseTagValue(char delimiter, bool resolveEntities, bool throwOnResolveFailure, TextEnum textEnum)
{
    size_t start = position_;
    std::string result = "";
    if (textEnum == TextEnum::TEXT && text_ != "") {
        result.append(text_);
    }
    while (true) {
        char cRecv = static_cast<char>(ParseTagValueInner(start, result, delimiter, textEnum, resolveEntities));
        if (cRecv == 0) {
            return result;
        } else if (cRecv == 1) {
            break;
        } else if (cRecv == 2) { // 2: break flag
            continue;
        } else if (!ParseTagValueFunc(cRecv, throwOnResolveFailure, textEnum, start, result)) {
            continue;
        }
        ++position_;
        result = result + static_cast<char>(cRecv);
        start = position_;
    }
    result.append(strXml_, start, position_ - start);
    return result;
}
  1. XmlPullParser::ParseNspFunc方法入参写法不一致
// 3.1release
void XmlPullParser::ParseNspFunc(size_t &i, std::string &attrName, bool &any)

// master
void XmlPullParser::ParseNspFunc(size_t &i, const std::string &attrName, bool &any)
  1. XmlPullParser::ParseNsp方法中清除了代码
bool XmlPullParser::ParseNsp()
{
    ...
    // 3.1release中存在此if判断,master中没有
    if (namespace_ == "") {
        namespace_ = "";
    }
    ...
}
  1. XmlPullParser::ParseNsp方法中清除了代码
bool XmlPullParser::ParseNsp()
{
    ...
    // 3.1release中存在此if判断,master中没有
    if (namespace_ == "") {
        namespace_ = "";
    }
    ...
}
  1. XmlPullParser::ParseEntityDecl
void XmlPullParser::ParseEntityDecl()
{
    ...
    // master分支中添加了此if代码,3.1release没有
    if (generalEntity && bDocDecl) {
        documentEntities[name] = entityValue;
    }
    ...
}

通过对比分析,与xml标签属性解析相关的有第1处和第2处,其余不同的地方均是代码写法上的优化,未改变相关逻辑,在3.1release代码中分别修改1、2处的代码编译后测试,修改第一处代码即可对xml的所有属性进行解析。

6 知识分享

在新的版本中可能已经修复了一些问题,合理的对比不同分支的代码可以更快的了解和解决问题。

...全文
81 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
源码链接: https://pan.quark.cn/s/0f7c75cb3aad ### MIPI Video Mode 与 Command Mode 的差异 #### 一、引言 MIPI (Mobile Industry Processor Interface) 是一种用于连接移动设备处理器及其外围设备的标准化接口。MIPI 接口支持多种协议,其包含 DSI (Display Serial Interface) 和 DCS (Display Control Interface) 等协议。在 MIPI 接口的应用,主要涉及两种工作模式:Video Mode(视频模式)和 Command Mode(命令模式)。本文旨在系统性地阐述这两种模式的工作机制、特性以及实际应用环境。 #### 二、LCD RAM 概念说明 在进一步探讨 MIPI 的两种模式之前,有必要对文提及的“LCD RAM”概念进行明确。实际上,“LCD RAM”并非一个通用术语,而是本文作者用来描述 LCD 控制器用于存储显示数据的内存区域。LCD(Liquid Crystal Display,液晶显示屏)通常配备一个控制 IC(Integrated Circuit,集成电路),该控制 IC 可能内置 RAM 以缓存显示数据。 #### 三、MIPI Video Mode(视频模式) **定义:** - 视频模式是一种类似于传统 RGB 接口的工作模式,它要求主机持续不断地向显示器传输刷新数据。 - 在这种模式下,数据和控制信号以报文的形式通过 MIPI 总线进行传输。 - 显示器本身无需配备帧缓冲器,因为主机会周期性地刷新屏幕。 **特点:** 1. **实时性高:** 主机需要不...
源码下载地址: https://pan.quark.cn/s/a4b39357ea24 在电磁模拟技术,CST(Computer Simulation Technology)是一种被广泛采纳的软件工具,它主要用于电磁场、微波、天线以及射频系统的设计工作。本资料将详细分析CST软件离散端口的具体配置方法,这些方法对于提升仿真结果的精确度和专业水准具有决定性作用。离散端口在CST软件扮演着模拟信号输入或输出的重要角色,它们构成了仿真模型不可或缺的部分。在配置离散端口时,一个核心的原则是保证端口的方向与网格线保持一致,这是因为这样做能够有效降低计算过程产生的误差,并确保仿真数据的有效性。如果未能遵循这一指导原则,可能会引发未知的计算问题,进而导致仿真结果失去可靠性。 在CST软件配置离散端口,通常需要借助“Pick Points”这一功能。通过选择“Pick Edge Center”选项,端口将被设定在模型边缘的心位置上。然而,这种做法并不总是能够确保端口与网格线保持平行。在某些特定情形下,模型的几何构造可能不允许直接选取一个与网格线平行的边作为端口的安装位置。 为了克服这一挑战,可以采用多种不同的策略。如果模型本身已经包含一条与馈电口平行的边,那么可以直接利用这条边来建立端口,此时CST软件会自动调整端口使其与网格线对齐。另一种可选的方法是,当模型不具备现成的平行边时,用户可以手动构建一个几何结构,比如一个立方体,并使其边缘与馈电口平行。通过这种方式,新建立的几何结构的边缘就可以作为端口的位置,从而确保端口与网格线的平行关系。 在实施上述操作时,必须关注端口尺寸的合理性和物理意义的一致性。端口的尺寸应当依据实际天线馈电部分的尺寸进行适当调整,过大的端口或...
代码下载链接: https://pan.quark.cn/s/a4b39357ea24 【使用TensorFlow进行图像识别】 图像识别作为计算机视觉领域的关键任务之一,其核心在于通过算法解析和理解图像所包含的信息。在此资源,我们将集探讨如何借助功能强大的深度学习框架TensorFlow来执行手写数字识别。手写数字识别构成了众多实际应用的基础,例如自动支票处理、光学字符识别(OCR)等场景。 TensorFlow是由Google创建的一个开源库,它主要用于数值运算和机器学习,尤其在深度学习方面表现卓越。其核心优势在于可以构建并训练复杂的神经网络架构,并且在多种硬件环境实现高效执行,涵盖CPU和GPU平台。 在此实践项目,我们将运用TensorFlow来构建一个卷积神经网络(CNN)模型,这种架构是处理图像数据的理想选择。CNNs通过模仿人脑视觉皮层的运作机制,能够自主地提取图像的关键特征,进而达成识别目标。在手写数字识别的特定情境下,这些特征可能涉及笔画的几何形态、走向以及相互间的连接模式。 对于CNN的基础结构,我们需要具备相应的认知,其通常由卷积层、池化层、全连接层以及激活函数等部分组成。卷积层借助滤波器(亦称卷积核)对图像进行扫描,以捕捉局部特征;池化层则用于降低数据维度,同时保留核心信息;全连接层将特征向量映射至各类别的概率分布;而激活函数如ReLU则通过引入非线性元素,使模型能够学习更为复杂的模式。 在此案例,建议采用MNIST数据集,这是一个广泛用于手写数字识别的标准测试集。该数据集包含60,000个训练样本和10,000个测试样本,每个样本均为28x28像素的灰度图像,代表0到9这十个数字的某一个。为了训练模型,必须首先加载数据,并...

580

社区成员

发帖
与我相关
我的任务
社区描述
OpenHarmony开发者社区
其他 企业社区
社区管理员
  • csdnsqst0025
  • shewaliujingli
  • BaoWei
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧