详解将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的大致目录如下图。

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

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

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

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

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库。

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

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

注意,这里的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库如下图所示:

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库。没听懂?看下图:

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

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

如下图所示:

接下来编写配置CMakeList.txt:

# For more information about using CMake with Android Studio,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,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'
}

效果如图:

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

版权所有,尊重版权。
微信公众号:

相关文章

  jsonp需要在页面中添加一个<script>元素,由该元素来从其他服务器加载json数据。 <body&g...
<script> var testApi = "地址"; $.ajax({ url:testApi,//可以不是本地域名 type:‘post...
总是有人会遇到跨域问题,然后有个jsonp的解决方案,MVC中代码如下: public class JsonpResult : Syst...
最近开发中遇到调用第三方web_api的功能,后端在处理json数据时使用fastjson来做反序列化,由于调用api...
JSON全称为JavaScript ObjectNotation,它是一种轻量级的数据交换格式,易于阅读、编写、解析。jsoncpp...
JsonSerializer有多个属性,用于自定义如何序列化JSON。这些也可以通过JsonSerializerSettings参数,在...