Json格式作为input文件格式,开源库nlohmann/json教程,包括静态变量

当我们有很多参数需要读入程序时,可以将这些参数写在一个input文件中,程序启动后读入。如果输入的参数还具有一定的层级关系而变得复杂时,那就可以考虑使用json格式的输入文件了。例如下面这样的输入:

{
  "pi": 3.141,
  "happy": true,
  "name": "Niels",
  "nothing": null,
  "answer": {
    	"everything": 42
  },
  "list": [1, 0, 2],
  "object": {
	    "currency": "USD",
 	    "value": 42.99
  }
}

本文推荐一个非常好用的json文件读写开源库,并介绍一些满足大多数人使用场景的基础用法。

开源库:nlohmann/json,地址:https://github.com/nlohmann/json

安装

安装方式非常简单,将库中的nlohmann文件夹拷贝到/usr/include中即可(linux操作系统)。

使用

在主函数文件中引用头文件:

#include "nlohmann/json.hpp"
using json = nlohmann::json;

在讲怎样从文件中读取json数据之前,先说说开源库中定义的json对象。

  1. 定义json对象

       // 定义json对象,像初始化c++map类型对象一样初始化json对象
       json j;
       j["pi"] = 3.14;
       j["name"] = "Niels";
       j["if_happy"] = true;
       j["nothing"] = nullptr;
       j["book"]["id"] = 12345;
       j["book"]["price"] = 12.5;
       j["list"] = {1,0,3};
       j["object"] = {{"currency","usd"},{"value",22.5}};
    

    此时json对象j已经含有上述初始化的各个成员,成员的获取如下:

       // 成员获取示例1
       double num_pi;
       num_pi = j["pi"].get<double>();
       // 成员获取示例2
       auto money = j["object"];
       
       cout<<num_pi<<endl<<money<<endl;
    
  2. c++ string对象与json对象的数据交换

       // 使用string对象解析获得json对象,用到json::parse(string s)函数
       string s;
       // 从json对象获得对象的内容并放入string对象中(术语叫序列化),数字4指的是对象输出的字符串换行的缩进为4
       s = j.dump(4);      
       cout<<s<<endl;
    
       // R开头的字符串表示字符串内容不做转义
       s= R"(
    	  {
    		 "name":"Niels",
    		 "id":12345
    	  }
       )";
       // 从string对象中获得内容解析放入json对象中
       j = json::parse(s);
       cout<<j.dump(2)<<endl;
    
  3. json对象的文件读入与输出

    // 终于到了文件了,这部分是大部分人需要用到的,就是从文件中读入json,或者输出json格式的文件
       ofstream os{"out.plt"};
       if(!os) cout<<"file open failed"<<endl;
       // 输出到文件,可以直接用<<运算符结合stew(4)来输出,也可以先dump成string对象再输出
       os<<j.dump(4)<<endl;
       os.close();
    
       ifstream is{"out.plt"};
       if(!is) cout<<"input file open failed"<<endl;
       // 从文件中读入
       is>>j;
       cout<<setw(4)<<j<<endl;
       is.close();
    
  4. 枚举变量在json对象中怎么表示
    由于默认情况下json把枚举序列化为整型,可能会出现错误,所以需要使用宏定义NLOHMANN_JSON_SERIALIZE_ENUM来把枚举变量和string绑定在一起,且定义一个无效的枚举变量来防止错误,枚举这块比较绕,可以参考链接:https://json.nlohmann.me/features/enum_conversion/

    // example enum type declaration
    enum class TaskState {
      TS_STOPPED,
      TS_RUNNING,
      TS_COMPLETED,
      TS_INVALID=-1,
    };
    
    // map TaskState values to JSON as strings
    NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, {
      {TaskState::TS_INVALID, nullptr},
      {TaskState::TS_STOPPED, "stopped"},
      {TaskState::TS_RUNNING, "running"},
      {TaskState::TS_COMPLETED, "completed"},
    })
       
    s = R"({
      "task":"running"
    })";
    // json key:value对的value现在用宏定义绑定的string来赋值,json对象自动会将其匹配到对应的枚举变量
    j = json::parse(s);
    TaskState task=j["task"];
    cout<<static_cast<int>(task)<<endl;
    
  5. 用户自定义类对象与json对象的数据互传

    如果用户自定义的类对象需要从json对象读入成员变量的值,或者从对象中抽出数据到json对象中,可以定义两个名为to_json()和from_json()的函数,即可直接使用 = 赋值运算符来操作json对象和用户自定义对象的互相转换了

    class person{
    public:
       string name;
       int age;
    };
    person p{"Taylor",18};
    j = p;
    cout<<j<<endl;
    j["age"]= 20;
    p = j;
    cout<<"p.age = "<<p.age<<endl;
    

    上面这个方法已经够简单了,但是Lohmann还提供了更方便的宏定义,只需使用
    LOHMANN_DEFINE_TYPE_NON_INTRUSIVE(类名字,依次写出和json对象共有的成员变量)即可,不用写出to_json()和from_json()。具体本文最后给出的示例参考文件开头部分的宏定义:

    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, age);
    

总结

全部代码如下:

/**
 * 编译命令:g++ -std=c++11 test.cpp -o xx
 */
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include "nlohmann/json.hpp"
using json=nlohmann::json;
using namespace std;

class person{
public:
   string name;
   int age;
};

// 方式1 , 如果使用该方式,将下面 #if 0 改为 #if 1
#if 0
void to_json(json& j, const person& p)
{
   j = json{{"name", p.name}, {"age",p.age}};
}
void from_json(const json& j, person& p)
{
   p = person{j["name"].get<string>(), j["age"].get<int>()};
}
#else
// 方式2
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, age);
#endif

// example enum type declaration
enum class TaskState {
    TS_STOPPED,
    TS_RUNNING,
    TS_COMPLETED,
    TS_INVALID=-1,
};

// map TaskState values to JSON as strings
NLOHMANN_JSON_SERIALIZE_ENUM( TaskState, {
    {TaskState::TS_INVALID, nullptr},
    {TaskState::TS_STOPPED, "stopped"},
    {TaskState::TS_RUNNING, "running"},
    {TaskState::TS_COMPLETED, "completed"},
})


int main()
{
   cout<<"--------------------- part 1 -------------------"<<endl;
   // 定义json对象,像初始化c++map类型对象一样初始化json对象
   json j;
   j["pi"] = 3.14;
   j["name"] = "Niels";
   j["if_happy"] = true;
   j["nothing"] = nullptr;
   j["book"]["id"] = 12345;
   j["book"]["price"] = 12.5;
   j["list"] = {1,0,3};
   j["object"] = {{"currency","usd"},{"value",22.5}};
   cout<<setw(4)<<j<<endl;

   // 成员获取示例1
   double num_pi;
   num_pi = j["pi"].get<double>();
   // 成员获取示例2
   auto money = j["object"];
   
   cout<<num_pi<<endl<<money<<endl;

   cout<<"--------------------- part 2 -------------------"<<endl;
   // 使用string对象解析获得json对象,用到json::parse(string s)函数
   string s;
   // 从json对象获得对象的内容并放入string对象中(术语叫序列化),数字4指的是对象输出的字符串换行的缩进为4
   s = j.dump(4);      
   cout<<s<<endl;

   // R开头的字符串表示字符串内容不做转义
   s= R"(
      {
         "name":"Niels",
         "id":12345
      }
   )";
   // 从string对象中获得内容解析放入json对象中
   j = json::parse(s);
   cout<<j.dump(2)<<endl;

   cout<<"--------------------- part 3 -------------------"<<endl;
   // 终于到了文件了,这部分是大部分人需要用到的,就是从文件中读入json,或者输出json格式的文件
   ofstream os{"out.plt"};
   if(!os) cout<<"file open failed"<<endl;
   // 输出到文件,可以直接用<<运算符结合stew(4)来输出,也可以先dump成string对象再输出
   os<<j.dump(4)<<endl;
   os.close();

   ifstream is{"out.plt"};
   if(!is) cout<<"input file open failed"<<endl;
   // 从文件中读入
   is>>j;
   cout<<setw(4)<<j<<endl;
   is.close();
   // 如果用户自定义的类对象需要从json对象读入成员变量的值,或者从对象中抽出数据到json对象中,可以定义两个名为
   // to_json()和from_json()的函数,即可直接使用 = 赋值运算符来操作json对象和用户自定义对象的互相转换了
   person p{"Taylor",18};
   j = p;
   cout<<j<<endl;
   j["age"]= 20;
   p = j;
   cout<<"p.age = "<<p.age<<endl;
   /* 上面这个方法已经够简单了,但是Lohmann还提供了更方便的宏定义,只需使用
    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(类名字,依次写出和json对象共有的成员变量);即可不用写出to_json()和from_json()
    具体示例参考文件开头部分的宏定义*/

   cout<<"--------------------- part 4 -------------------"<<endl;
   /* 最后一个部分了,讲一下枚举变量在json对象中的用法
      由于默认情况下json把枚举序列化为整型,可能会出现错误,所以需要使用宏定义来把枚举变量和string绑定在一起,且定义一个
      无效的枚举变量来防止错误*/
   // 枚举这块比较绕,可以参考链接:https://json.nlohmann.me/features/enum_conversion/
   s = R"({
      "task":"running"
   })";
   // json key:value对的value现在用宏定义绑定的string来赋值,json对象自动会将其匹配到对应的枚举变量
   j = json::parse(s);
   TaskState task=j["task"];
   cout<<static_cast<int>(task)<<endl;

   return 0;
}

靓仔,你学费了吗?有问题评论区见~

参考:

https://blog.youkuaiyun.com/u011341856/article/details/108797920

https://json.nlohmann.me/features/enum_conversion/

<think>嗯,用户问的是如何用JSON文件实现不同语言之间的转换。首先,我需要理解用户的需求。可能他们正在开发一个多语言支持的应用程序,需要管理不同语言的文本,比如按钮标签、菜单项或者提示信息。用户可能希望有一个简单的方法来存储和加载这些翻译,而不想依赖复杂的数据库或者外部服务。 接下来,我得考虑JSON在这种场景下的应用。JSON结构简单,容易读写,适合存储键值对的翻译内容。比如,每种语言对应一个JSON文件,键是原文,值是对应的翻译。例如,en.json里有"welcome": "Welcome",而fr.json里有"welcome": "Bienvenue"。 然后,我需要思考如何实现这个系统。可能需要一个语言管理器,根据用户选择的语言加载对应的JSON文件。同时,在代码里引用这些键,动态替换文本。此外,可能还要处理动态内容,比如带变量的句子,比如"hello {name}",这时候需要字符串插值。 用户可能还关心如何维护这些JSON文件。比如,当添加新功能时,如何确保所有语言文件都更新了对应的键。可能需要一些自动化工具来检查缺失的翻译或者同步键的变化。 另外,扩展性也是一个问题。如果应用支持很多语言,手动管理JSON文件可能会变得繁琐。这时候可能需要一个翻译管理系统或者使用第三方服务,但用户可能希望保持简单,所以还是基于文件的方法。 还要考虑技术栈。比如,前端可以用JavaScript动态加载JSON,后端可以用Node.js、Python等处理。可能还需要考虑文件的结构,比如按模块或页面组织翻译内容,避免单个文件过大。 可能存在的挑战包括如何处理复数形式、性别差异等复杂翻译情况,这时候简单的键值对可能不够,需要更复杂的结构。但用户的问题可能暂时不需要处理这些复杂情况,所以保持简单即可。 最后,我需要总结步骤,给出一个清晰的例子,说明如何创建JSON文件、加载它们,并在应用中使用。同时,提到可能的优化和扩展方向,比如自动化工具或集成翻译API,帮助用户进一步探索。使用</think>JSON文件实现不同语言间的转换(即**多语言国际化/i18n支持**)是一种常见且高效的方式。以下是具体的实现思路和步骤,并附上示例: --- ### **核心思路** 1. **为每种语言创建独立的JSON文件**,存储对应语言的翻译内容(键值对)。 2. **通过程序动态加载对应语言的JSON文件**,根据用户选择的语言替换界面中的文本。 3. **键(Key)保持一致**,值(Value)为不同语言的翻译内容。 --- ### **实现步骤与示例** #### **1. 创建语言JSON文件** 为每种语言创建独立的JSON文件,例如: - `en.json`(英语) ```json { "welcome": "Welcome", "button_submit": "Submit", "error_message": "Please check your input: {field}" } ``` - `zh-CN.json`(简体中文) ```json { "welcome": "欢迎", "button_submit": "提交", "error_message": "请检查输入项:{field}" } ``` - `fr.json`(法语) ```json { "welcome": "Bienvenue", "button_submit": "Soumettre", "error_message": "Veuillez vérifier le champ : {field}" } ``` --- #### **2. 加载语言文件** 根据用户选择的语言(如通过浏览器设置、用户手动切换),动态加载对应的JSON文件。 **示例代码(JavaScript)**: ```javascript // 定义当前语言(可从浏览器或用户设置获取) let currentLang = navigator.language || 'en'; // 例如 'zh-CN' // 异步加载对应语言的JSON文件 async function loadTranslations(lang) { const response = await fetch(`/locales/${lang}.json`); return await response.json(); } // 使用示例 loadTranslations(currentLang).then(translations => { // 将翻译内容存储到全局变量或状态管理工具 window.translations = translations; }); ``` --- #### **3. 实现翻译函数** 通过键名获取对应语言的翻译内容,并支持动态参数(如占位符替换)。 **示例代码(JavaScript)**: ```javascript function t(key, params = {}) { let text = window.translations[key] || key; // 若未找到翻译,默认返回键名 // 替换占位符(例如 {field} → params.field) Object.keys(params).forEach(k => { text = text.replace(`{${k}}`, params[k]); }); return text; } // 使用示例 document.getElementById('welcome').innerText = t('welcome'); // 输出 "欢迎" document.getElementById('error').innerText = t('error_message', { field: '邮箱' }); // 输出 "请检查输入项:邮箱" ``` --- #### **4. 在界面中使用翻译** 在HTML或前端框架(如React/Vue)中绑定翻译内容: - **HTML示例**: ```html <h1 id="welcome"></h1> <button>{{ t('button_submit') }}</button> ``` - **React示例**: ```jsx function App() { return ( <div> <h1>{t('welcome')}</h1> <button>{t('button_submit')}</button> </div> ); } ``` --- ### **优化与扩展** 1. **自动化工具**: - 使用库如 `i18next`(支持React/Vue等框架)或 `react-intl` 简化多语言管理。 - 示例: ```javascript // 使用i18next import i18n from 'i18next'; i18n.init({ lng: 'zh-CN', resources: { en: { translation: enJSON }, 'zh-CN': { translation: zhCNJSON } } }); // 使用:i18n.t('welcome'); ``` 2. **动态切换语言**: - 提供下拉菜单让用户切换语言,重新加载翻译文件并更新界面。 3. **缺失翻译回退**: - 如果当前语言缺少某个键,自动回退到默认语言(如英语)。 4. **支持复数形式**: - 在JSON中定义不同复数形式的翻译,例如: ```json { "item_count": "{count} item |||| {count} items" } ``` --- ### **适用场景** - 网站/App的静态文本国际化(如按钮、标题)。 - 动态内容的本地化(如日期、货币格式)。 - 结合CMS(内容管理系统)实现翻译内容的动态更新。 --- 通过JSON管理多语言,既能保持代码与内容的分离,也便于非开发者(如翻译人员)直接编辑语言文件。如果需要更复杂的逻辑(如动态加载、缓存),可以结合后端API或云服务(如AWS S3存储语言文件)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值