• 【OpenHarmony-NDK技术】简单将cJson移植到OpenHarmony中,并在c层修改参数值再返回json


    1、cJson的简单介绍

    cJson - github网址

    概述

    一般使用cJson是,需要将json文本转化为json对象–编码,将json对象转化为json文本–解析。
    git clone https://github.com/DaveGamble/cJSON.git
    后留意cJSON.h和cJSON.h两个文件。

    1、cJson的介绍
    cJson是一个数据结构,里面含有prev next child 等指针用于获取json数据的具体值
    typedef struct cJSON
    {   
    	struct cJSON *next;
        struct cJSON *prev;
        struct cJSON *child;
        //type是指,该节点的数据类型 
        int type;
        //字符串获取的变量
        char *valuestring;
        //int值获取的变量
        int valueint;
        //double 值获取的变量
        double valuedouble;
        //暂未接触
        char *string;
    } cJSON;
    
    //int type的值,去这几种
    //#define cJSON_Invalid (0)
    //#define cJSON_False  (1 << 0)
    //#define cJSON_True   (1 << 1)
    //#define cJSON_NULL   (1 << 2)
    //#define cJSON_Number (1 << 3)
    //#define cJSON_String (1 << 4)
    //#define cJSON_Array  (1 << 5)
    //#define cJSON_Object (1 << 6)
    //#define cJSON_Raw    (1 << 7)
    
    //编码方法
    cJson* cJSON_Parse(char *); 
    //解码方法
    char* cJSON_Print(cJson *); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    2、编码
    //1、声明一个简单的字符串
    char *jsonText = "{\"name\":\"zhangsan\",\"age\":18,\"price\":21.0}";
    //2、将字符串给到cJSON_Parse方法,json!=null,就会给到一个cJson结构。
    cJSON *json = cJSON_Parse(jsonText);
    
    // 获取到string的值
    json->valuestring
    // 获取到string的值
    json->valuestring
    // 获取到int的值
    json->valueint
    // 获取到int的值
    json->valuedouble
    
    //以上三种方法获取对应类型的字段的值
    
    
    // 3、通过根json去获取到具体的一个字段(name)
    cJSON *jsonValue = cJSON_GetObjectItem(json, "name");
    //返回的也是一个cJson结构,获取值与上面相同操作 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    3、解码
    //1.将一个cJOSN的结构,变量一个json文本
    char * cJSON_Print(cJSON *);
    
    • 1
    • 2

    2、OpenHarmony项目实现

    1、项目创建

    IDE版本:
    DevEco Studio 3.1.1 Release
    Build Version: 3.1.0.501, built on June 20, 2023
    Build #DS-223.8617.56.36.310501
    Runtime version: 17.0.6+10-b829.5 amd64

    按照官方的要求创建一个Native项目

    2、目录及文件介绍

    在这里插入图片描述

    1、cJSON.c是cjson实现的源码
    2、cJSON.h是cJSON.c的头文件,以上两个文件可以在上面git中获取。
    3、CMakeList.exe 文件时配置cmake编译的配置文件
    # the minimum version of CMake.
    cmake_minimum_required(VERSION 3.4.1)
    project(MyFirstCpp)
    
    # 定义一个变量,并赋值为当前模块cpp目录
    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
    
    # 添加头文件.h目录,包括cpp,cpp/include,告诉cmake去这里找到代码引入的头文件
    include_directories(${NATIVERENDER_ROOT_PATH}
                        ${NATIVERENDER_ROOT_PATH}/include)
    # hiloglib -- 给c层打印日志使用的
    find_library(
        hilog_lib
        hilog_ndk.z
    )
    # 声明一个产物libentry.so,SHARED表示产物为动态库,hello.cpp为产物的源代码
    # !!!注意:如果使用源码一起编译是,需要将源文件添加进来,例如:hello.cpp cjson.c
    add_library(entry SHARED hello.cpp cjson.c)
    
    # 声明产物entry链接时需要的三方库libace_napi.z.so
    # 这里直接写三方库的名称是因为它是在ndk中,已在链接寻址路径中,无需额外声明
    # 添加的编译so库
    target_link_libraries(entry PUBLIC ${hilog_lib} libace_napi.z.so)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    4、Index.ets 布局文件
    //伪代码
    import testNapi from 'libentry.so'
    json: string = "{\"name\":\"zhangsan\",\"age\":18,\"price\":21.5}";
    Button().onClick(() => {
      let json = testNapi.modifyJson(this.json,this.inputText);
      hilog.info(0x0000, 'testTag', 'cjson name = %{public}d', json);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    5、LogUtils.ts 是一个打印日志的工具
    export class LogUtils{
      static Tag:string = 'MyFirstCpp';
      static i(...args){
        hilog.info(0x0000, LogUtils.Tag, '%{public}s', args);
      }
      static d(msg:string){
        hilog.debug(0x0000, LogUtils.Tag, '%{public}s', msg);
      }
      static e(msg:string){
        hilog.error(0x0000, LogUtils.Tag, '%{public}s', msg);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    6、hello.cpp文件方法配置介绍

    初始化创建一个Native 项目在hello.cpp文件中默认存在如下代码:

    EXTERN_C_START
    static napi_value Init(napi_env env, napi_value exports) {
        napi_property_descriptor desc[] = {
            {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
            //{"modifyJson", nullptr, ModifyJson, nullptr, nullptr, nullptr, napi_default, nullptr},
        };
        
        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
        return exports;
    }
    EXTERN_C_END
    
    static napi_module demoModule = {
        .nm_version = 1,
        .nm_flags = 0,
        .nm_filename = nullptr,
        .nm_register_func = Init,
        .nm_modname = "entry",
        .nm_priv = ((void *)0),
        .reserved = {0},
    };
    
    extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {
        napi_module_register(&demoModule);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    其中 napi_property_descriptor 变量里面存在的是c对应js的方法。需要添加不同的方法(js调用的方法)需要在这里声明。
    {“add”, nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr}
    目前按照以上格式,替换一下【“add”】 和 【Add】的值。
    eg:
    我现在要声明一个modifyJson方法,如下参数,js层也一并需要声明。

    {"modifyJson", nullptr, ModifyJson, nullptr, nullptr, nullptr, napi_default, nullptr}
    
    • 1

    在js层也需要声明条用的方法如下:
    在src/main/cpp/types/libentry/index.d.ts文件下:

    export const add: (a: number, b: number) => number;
    
    • 1

    eg:
    添加 modifyJson 方法

    export const add: (a: number, b: number) => number;
    //json 是json字符串;nameValue 是修改name字段的值 
    export const modifyJson:(json:string,nameValue:string)=>string;
    
    • 1
    • 2
    • 3
    3、代码介绍

    首先NDK开发使用的是js的napi库。需要注意的是js和C/C++之间的数据类型之间的切换。

    1、napi与C/C++之间数据类型切换

    anpi 中的数据目前使用一个napi_value (js层接收的类型)
    C/C++中的数据类型目前用到了 string,char*,int, double

    下面就是 anpi_value 转为 C/C++ string、int、double

    /**
     * 获取 js中的string 到 C中使用
     * @param env
     * @param value
     * @return 
     */
    static const string get_native_string_value(napi_env env,napi_value value){
    	//不知道字符串长度,先获取一下,拿到字符串的真实长度
        size_t textLen;
        napi_get_value_string_utf8(env, value, NULL, 0, &textLen);
        
        //得到了真实的长度,需要在次长度上+1(不全\0)
        char text[textLen+1];
        napi_get_value_string_utf8(env, value,text, sizeof(text), &textLen);
        //返回的是std:string,记得导入 using namespace std;
        string buf(text, textLen);
        return buf;
        
    }
    
    /**
     * 获取 js 中的 int 到 C中使用
     * @param env
     * @param _msg
     * @return 
     */
    static int get_native_int_value(napi_env env,napi_value value){
        int32_t iNum;
        napi_get_value_int32(env, value, &iNum);
        return iNum;
    }
    
    /**
     * 获取 js 中的double 到C中使用
     * @param env
     * @param _msg
     * @return 
     */
    static double get_native_double_value(napi_env env,napi_value value){
        double dNum;
        napi_get_value_double(env, value, &dNum);
        return dNum;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    下面就是 C/C++ string、int、double 转为 napi_value(可以直接返回给js层的类型)

    /**
     * 将c中的char* 构建一个 js的string类型
     * @return 
     */
    static napi_value return_string_value(napi_env env,char *_msg){
        napi_value msg;
        napi_create_string_utf8(env, _msg, strlen(_msg), &msg);
        return msg;
    }
    
    /**
     * 将c中的int 构建一个 js的int类型
     * @return 
     */
    static napi_value return_int_value(napi_env env,int _msg){
        napi_value i;
        napi_create_int32(env, _msg, &i);
        return i;
    }
    
    /**
     * 将c中的double 构建一个 js的double类型
     * @return 
     */
    static napi_value return_double_value(napi_env env,double _msg){
        napi_value d;
        napi_create_double(env, _msg, &d);
        return d;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    4、核心处理
    static napi_value ModifyJson(napi_env env, napi_callback_info info) {
        napi_value modifyJson;
    	// OH_LOG_INFO c的日志,需要按照CmakeList.txt 导入库。不然无法访问
        OH_LOG_INFO(LOG_APP,"ModifyJson start");
    
        //1、获取js中传过来的参数,放入 args 数组中,argc 是有两个参数
        size_t argc = 2;
        napi_value args[2] = {nullptr};
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
        
        //2.获取对应参数的值
        string jsonText = get_native_string_value(env, args[0]);
        //3.获取修改的文本
        string newName = get_native_string_value(env, args[1]);
        //4.编码json
        cJSON *jsonObj = cJSON_Parse(jsonText.c_str());
        //5.获取字段为 name 的json对象,然后操作这个对象获取值和修改值
        cJSON *nameValueObj = cJSON_GetObjectItemCaseSensitive(jsonObj, "name");
        if(NULL == nameValueObj){
        	//Undefined_value 和 get_native_string_value,类似只是返回 char* -> napi_value 
            return Undefined_value(env, "NULL == nameValueObj");
        }
        
        //6.修改 nameValueObj 值,注意:赋值要使用 strdup 方法进行包一层,不然在 cJSON_Delete 释放时会报错
        //之前一个是将char* 赋值到valuestring 中,不进行strdup释放的时候回报错
    //    nameValueObj->valuestring = strdup(newName.c_str());
    	//查看cjson源码发现,自带一个修改指定字段的值 cJSON_SetValuestring
        cJSON_SetValuestring(nameValueObj, newName.c_str());
        //7.修改完值后需要重新进行对 jsonObj 进行 解码 json 再返回json到js层
        char *newJson = cJSON_Print(jsonObj);
        //8、返回新的json字符串
        napi_create_string_utf8(env, newJson, strlen(newJson), &modifyJson);
            
        //释放 jsonObj
        cJSON_Delete(jsonObj);
        return modifyJson;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    以上就是简单的将cJson 移植到OpenHarmony。希望可以帮助到大家!!!

  • 相关阅读:
    界面组件DevExpress WPF TreeView中文指南 - 如何实现数据分层展示
    《第一堂棒球课》:王牌一垒手·棒球3号位
    集合—Set接口实现类-LinkedHashSet
    内核级流量治理引擎Kmesh八大新特性解读
    Linux 内存之vmstat
    Vue3.0的设计目标是什么?做了哪些优化
    Windows下Nacos安装
    解析富有童趣的人工智能早教机器人
    深入理解多线程(第三篇)
    zookeeper的ZAB协议的原理以及底层源码实现超级详解
  • 原文地址:https://blog.csdn.net/sinat_35845281/article/details/138030855