详解将jsoncpp编译so库,进行封装和解析JSON数据

Posted 谭东jay

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解将jsoncpp编译so库,进行封装和解析JSON数据相关的知识,希望对你有一定的参考价值。

时间:2017年9月21日

环境:Windows 8.1

NDK版本:android-ndk-r14b

工具:Android Studio2.3.3


有的时候我们需要在封装的so库里把要获取到的信息转为json格式输出,或者解析外部传入进来的json数据,那么C++里一般使用jsoncpp库来实现。

jsoncpp 是比较出名的 C++ JSON 解析库。在 JSON官网也是首推的。源码下载链接:http://jsoncpp.sourceforge.net/

下面就给大家讲解jsoncpp的编译和简单使用。编译出的so库可以供Android使用。其他平台大同小异。

1、下载jsoncpp源码。可以在github上下载到:https://github.com/open-source-parsers/jsoncpp

下载后,jsoncpp的大致目录如下图。

详解将jsoncpp编译so库,进行封装和解析JSON数据

主要关注include和src目录下的lib_json这两个文件夹。

include里是.h头文件,src目录下的lib_json里是.cpp源文件。

详解将jsoncpp编译so库,进行封装和解析JSON数据


详解将jsoncpp编译so库,进行封装和解析JSON数据


2、用Android Studio新建一个支持c++的项目。在项目的libs目录下新建libjson文件夹,将include里的json文件夹里的.h头文件拷贝进去,并将src/lib_json下的.cpp和.inl及.h文件也拷贝进去。

详解将jsoncpp编译so库,进行封装和解析JSON数据

导入后可能会报错,引入的头文件路径不对,那么就按照提示进行修改和导入相应的头文件即可。

3、接下来,我们需要把我的源文件加入到列表,供CMake编译。编辑我们的CMakeLists.txt,如下面所示。


# For more information about using CMake with Android Studio, read the

# documentation: https://d.android.com/studio/projects/add-native-code.html


# Sets the minimum version of CMake required to build the native library.


cmake_minimum_required(VERSION 3.4.1)


# Creates and names a library, sets it as either STATIC

# or SHARED, and provides the relative paths to its source code.

# You can define multiple libraries, and CMake builds them for you.

# Gradle automatically packages shared libraries with your APK.



add_library( json

             SHARED

             libs/include/libjson/json_reader.cpp

             libs/include/libjson/json_value.cpp

             libs/include/libjson/json_writer.cpp)


add_library( # Sets the name of the library.

             native-lib


             # Sets the library as a shared library.

             SHARED


             # Provides a relative path to your source file(s).

             src/main/cpp/native-lib.cpp )


# Searches for a specified prebuilt library and stores the path as a

# variable. Because CMake includes system libraries in the search path by

# default, you only need to specify the name of the public NDK library

# you want to add. CMake verifies that the library exists before

# completing its build.


find_library( # Sets the name of the path variable.

              log-lib


              # Specifies the name of the NDK library that

              # you want CMake to locate.

              log )

set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../libs)

# Specifies libraries CMake should link to your target library. You

# can link multiple libraries, such as libraries you define in this

# build script, prebuilt third-party libraries, or system libraries.


target_link_libraries( # Specifies the target library.

                       native-lib

                       json

                       # Links the target library to the log library

                       # included in the NDK.

                       ${log-lib} )

4、编译我们的源码,生成相应的so库。


详解将jsoncpp编译so库,进行封装和解析JSON数据

5、编译好后,我们就可以查看到so库了。

如果编译中遇到这种错误提示,我们只需把include的头文件的<>改成“”即可。

详解将jsoncpp编译so库,进行封装和解析JSON数据


详解将jsoncpp编译so库,进行封装和解析JSON数据

注意,这里的json_tool.h要改下,正确的为:


// Copyright 2007-2010 Baptiste Lepilleur

// Distributed under MIT license, or public domain if desired and

// recognized in your jurisdiction.

// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE


#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED

#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED

#include <clocale>


/* This header provides common string manipulation support, such as UTF-8,

 * portable conversion from/to string...

 *

 * It is an internal header that must not be exposed.

 */


namespace Json {


/// Fallback for decimal_point on android, where the lconv is an empty struct.

    template<typename Lconv, bool=(sizeof(Lconv) >= sizeof(char*))>

    struct Locale {

        static char decimalPoint() {

            return '\0';

        }

    };


/// Return decimal_point for the current locale.

    template<typename Lconv>

    struct Locale<Lconv, true> {

        static char decimalPoint() {

            Lconv* lc = localeconv();

            if (lc == NULL) {

                return '\0';

            }

            return *(lc->decimal_point);

        }

    };


/// Converts a unicode code-point to UTF-8.

    static inline JSONCPP_STRING codePointToUTF8(unsigned int cp) {

        JSONCPP_STRING result;


        // based on description from http://en.wikipedia.org/wiki/UTF-8


        if (cp <= 0x7f) {

            result.resize(1);

            result[0] = static_cast<char>(cp);

        } else if (cp <= 0x7FF) {

            result.resize(2);

            result[1] = static_cast<char>(0x80 | (0x3f & cp));

            result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));

        } else if (cp <= 0xFFFF) {

            result.resize(3);

            result[2] = static_cast<char>(0x80 | (0x3f & cp));

            result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));

            result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));

        } else if (cp <= 0x10FFFF) {

            result.resize(4);

            result[3] = static_cast<char>(0x80 | (0x3f & cp));

            result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));

            result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));

            result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));

        }


        return result;

    }


/// Returns true if ch is a control character (in range [1,31]).

    static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }


    enum {

        /// Constant that specify the size of the buffer that must be passed to

        /// uintToString.

                uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1

    };


// Defines a char buffer for use with uintToString().

    typedef char UIntToStringBuffer[uintToStringBufferSize];


/** Converts an unsigned integer to string.

 * @param value Unsigned interger to convert to string

 * @param current Input/Output string buffer.

 *        Must have at least uintToStringBufferSize chars free.

 */

    static inline void uintToString(LargestUInt value, char*& current) {

        *--current = 0;

        do {

            *--current = static_cast<char>(value % 10U + static_cast<unsigned>('0'));

            value /= 10;

        } while (value != 0);

    }


/** Change ',' to '.' everywhere in buffer.

 *

 * We had a sophisticated way, but it did not work in WinCE.

 * @see https://github.com/open-source-parsers/jsoncpp/pull/9

 */

    static inline void fixNumericLocale(char* begin, char* end) {

        while (begin < end) {

            if (*begin == ',') {

                *begin = '.';

            }

            ++begin;

        }

    }


    static inline void fixNumericLocaleInput(char* begin, char* end) {

        char decimalPoint = Locale<struct lconv>::decimalPoint();

        if (decimalPoint != '\0' && decimalPoint != '.') {

            while (begin < end) {

                if (*begin == '.') {

                    *begin = decimalPoint;

                }

                ++begin;

            }

        }

    }


} // namespace Json {


#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED


为什么修改请看官方这里:https://github.com/open-source-parsers/jsoncpp/pull/523

修改方案看这里:https://github.com/open-source-parsers/jsoncpp/blob/52cfe5ae889adf0c4099b16ddeb4b529b1f09007/src/lib_json/json_tool.h
编译后的so库如下图所示:

详解将jsoncpp编译so库,进行封装和解析JSON数据


6、简单的使用方法:

#include <jni.h>

#include <string>

#include "../../../libs/include/libjson/value.h"


extern "C" {


JNIEXPORT jstring JNICALL

Java_com_tandong_jsoncpp_MainActivity_stringFromJNI(

        JNIEnv *env,

        jobject /* this */) {

    std::string hello = "Hello from C++";

    return env->NewStringUTF(hello.c_str());

}


JNIEXPORT jstring JNICALL

Java_com_tandong_jsoncpp_MainActivity_stringFromJNI2(

        JNIEnv *env,

        jobject /* this */) {

    Json::Value video;

    video["id"] = 1;

    video["name"] = "名字";

    const char *json_str = video.toStyledString().c_str();

    jstring result = env->NewStringUTF(json_str);

    return result;

}

}


注意jsoncpp不支持long型,支持double等其他类型。


7、如果我想把libjson.so放在其他项目引用使用怎么办?请往下看。假如我们新建个项目,需要用到libjson.so,并且重新编译使用了libjson.so库的so库。没听懂?看下图:

详解将jsoncpp编译so库,进行封装和解析JSON数据

先把上面编译后的libjson.so拷贝到新项目src/main/jniLibs/armeabi  里,即对应的so库文件夹里。

然后把jsoncpp源码include里的.h文件拷贝到新项目的libs/include/libjson/下面。

如下图所示:

详解将jsoncpp编译so库,进行封装和解析JSON数据


接下来编写配置CMakeList.txt:


# For more information about using CMake with Android Studio, read the

# documentation: https://d.android.com/studio/projects/add-native-code.html


# Sets the minimum version of CMake required to build the native library.


cmake_minimum_required(VERSION 3.4.1)


# Creates and names a library, sets it as either STATIC

# or SHARED, and provides the relative paths to its source code.

# You can define multiple libraries, and CMake builds them for you.

# Gradle automatically packages shared libraries with your APK.


set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../libs)



set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")



add_library( json

             SHARED

             IMPORTED )



set_target_properties( json

                       PROPERTIES IMPORTED_LOCATION

                       ../../../../src/main/jniLibs/armeabi/libjson.so )



add_library( # Sets the name of the library.

             native-lib


             # Sets the library as a shared library.

             SHARED


             # Provides a relative path to your source file(s).

             src/main/cpp/native-lib.cpp )


# Searches for a specified prebuilt library and stores the path as a

# variable. Because CMake includes system libraries in the search path by

# default, you only need to specify the name of the public NDK library

# you want to add. CMake verifies that the library exists before

# completing its build.


find_library( # Sets the name of the path variable.

              log-lib


              # Specifies the name of the NDK library that

              # you want CMake to locate.

              log )


# Specifies libraries CMake should link to your target library. You

# can link multiple libraries, such as libraries you define in this

# build script, prebuilt third-party libraries, or system libraries.


include_directories(libs/include)


target_link_libraries( # Specifies the target library.

                       native-lib

                       json

                       # Links the target library to the log library

                       # included in the NDK.

                       ${log-lib} )


然后编写jni的cpp方法:

#include <jni.h>

#include <string>

#include "../../../libs/include/libjson/value.h"


extern "C" {


JNIEXPORT jstring JNICALL

Java_com_tandong_jsoncppdemo_MainActivity_stringFromJNI(

        JNIEnv *env,

        jobject /* this */) {

    std::string hello = "Hello from C++";

    return env->NewStringUTF(hello.c_str());

}


JNIEXPORT jstring JNICALL

Java_com_tandong_jsoncppdemo_MainActivity_stringFromJNI2(

        JNIEnv *env,

        jobject /* this */) {

    Json::Value video;

    video["id"] = 1;

    video["name"] = "名字";

    const char *json_str = video.toStyledString().c_str();

    jstring result = env->NewStringUTF(json_str);

    return result;

}


}

再附上build.gradle的配置:

apply plugin: 'com.android.application'


android {

    compileSdkVersion 26

    buildToolsVersion "26.0.1"

    defaultConfig {

        applicationId "com.tandong.jsoncppdemo"

        minSdkVersion 15

        targetSdkVersion 26

        versionCode 1

        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        externalNativeBuild {

            cmake {

                cppFlags ""

            }

        }

        ndk{

            abiFilters 'armeabi'

        }

    }

    buildTypes {

        release {

            minifyEnabled false

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

        }

    }

    externalNativeBuild {

        cmake {

            path "CMakeLists.txt"

        }

    }

}


dependencies {

    compile fileTree(dir: 'libs', include: ['*.jar'])

    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {

        exclude group: 'com.android.support', module: 'support-annotations'

    })

    compile 'com.android.support:appcompat-v7:26.+'

    compile 'com.android.support.constraint:constraint-layout:1.0.2'

    testCompile 'junit:junit:4.12'

}

效果如图:



其他的编写方法一样,敬请关注。



版权所有,尊重版权。






以上是关于详解将jsoncpp编译so库,进行封装和解析JSON数据的主要内容,如果未能解决你的问题,请参考以下文章

如何在安卓ndk工程中引入jsoncpp

C++的Json解析库:jsoncpp和boost(转)

详细地jsoncpp编译方法 和 vs2010中导入第三方库的方法

基于开源库jsoncpp的json字符串解析

C/C++C++Json解析和生成的开源库:RapidJson和JsonCpp

12.6.2 常用的Json库