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

鸿蒙小紫娃 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 知识分享

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

...全文
12 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

421

社区成员

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

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