持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情
一、题目
描述:
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。
它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
因此,JSON格式的数据在程序中应用非常广泛。
现在拟做一个能够根据自定义的JSON模板字符串或文件,自动生成JSON文件的工具项目,可用来根据预定义的模板生成任意数量的随机 JSON 值。此工具可帮助开发人员和 QA 以各种输入值测试他们的产品。
为了方便使用,可以将生成的输出 JSON 另存为 .json 文件。
#include <iostream>
#include <random>
#include <fstream>
#include <ctime>
#include <stdint.h>
#include <list>
#include <vector>
using namespace std;
class JsonTemplate
{
public:
explicit JsonTemplate(const string& jsonTemplate):
jsonTemplate(jsonTemplate){}
string generate(){
}
void save(const string& filepath){
}
private:
string jsonTemplate;
};
int main(int, char**)
{
string temp =
R"({
"min_position": "int(3,4,5-9)\",
"has_more_items": "bool",
"items_html": "string('Bus','Car','Bike')\",
"new_latent_count": "int",
"data": {
"length": "int(20-30)\",
"text": "string"
},
"numericalArray": [
"repeat[5]",
"int(20,23-33)\"
],
"StringArray": [
"repeat[4]",
"string('Carbon','Nitrogen','Oxygen')\"
],
"multipleTypesArray": [3,"Hello",5,true],
"objArray": [
"repeat[5]",
{
"class": "string('middle','upper','lower')\",
"age": "int"
}
]
})";
for(int i = 0; i < 6; ++i){
temp = temp.replace(temp.find("\\"), 1, "");
}
JsonTemplate jt(temp);
cout << jt.generate() << endl;
jt.save("test.json");
return 0;
}
二、分析
首先我们知道JSON有三种类型: 对象、列表、值,要解析JSON模板,那就需要分别针对这三种类型进行独立的分析。
值类型是以"开头,和 [ 紧接着有效字符开头
列表类型是以 [ 开头,并且紧接着有个repeat[%d]的个数
对象类型是以 { 开头
这里值类型和列表类型都是以 [ 字符开头,需要区分一下
而列表类型和对象类型都是把特性解析出来之后,剩下的还是要交给值类型处理函数去解析,所以重点还是值类型的解析
值类型有六种: int(),string(),int,string,bool,固定值列表
通过第一个字符可以区分4种,但是int()和int,string()和string还需要判断是否有括号来判断
int()的特殊处理是会有5-9这种范围写法,需要解析成5,6,7,8,9
string的特殊处理是模板里字符串是用单引号包含起来的,而json字符串是以双引号包含起来的
列表类型的特殊处理是,每循环一个值之后,需要加一个逗号
剩下的就是按部就班,一块一块的处理就行了
三、模拟
- "min_position": "int(3,4,5-9)"
这是int()值类型,并且有5-9范围定义,先化简为int(3,4,5,6,7,8,9),然后再通过随机0-6的数通过数组下标索引就好。 - "has_more_items": "bool"
这是bool类型,随机0-1的数,0就是false,1就是true。 - "items_html": "string('Bus','Car','Bike')"
这是string()类型,有3个值,随机0-2的数通过数组下标索引就好。 - "new_latent_count": "int"
这是int类型,没有范围,就取一个随机int数就好。 - "text": "string"
这是string类型,随机字符串就好。 - "multipleTypesArray": [3,"Hello",5,true]
这是固定值列表类型,有4个值,,随机0-3的数通过数组下标索引就好。
四、实现
class JsonTemplate
{
public:
explicit JsonTemplate(const string& jsonTemplate):
jsonTemplate(jsonTemplate), engine(static_cast<unsigned int>(time(nullptr))){}
string generateValue(string& values){
if(values.at(0) == 'b'){
return (random(1) > 0 ? "true" : "false");
}
bool isInt = (values.at(0) == 'i');
size_t start = values.find('(');
if(start > values.size()){
if(isInt){
return to_string(random(1000));
}else{
string r;
r.push_back('"');
r.push_back(random('Z' -'A') + 'A');
r.push_back('"');
return r;
}
}else{
vector<string> datas;
string data;
for(int i = start + 1; i < values.size() - 1; ++i){
if(values.at(i) != ','){
data.push_back(values.at(i));
}else{
datas.push_back(data);
data.clear();
}
}
datas.push_back(data);
if(isInt){
for(int i = datas.size() - 1; i >= 0; --i){
size_t index = datas.at(i).find('-');
if(index > datas.at(i).size()) continue;
int start = atoi(datas.at(i).substr(0,index).data()),
end = atoi(datas.at(i).substr(index + 1, datas.at(i).size() - index - 1).data());
datas[i] = to_string(start);
for(int j = start + 1; j <= end; ++j){
datas.push_back(to_string(j));
}
}
}else{
for(int i = datas.size() - 1; i >= 0; --i){
datas[i][0] = '"';
datas[i][datas.at(i).size() - 1] = '"';
}
}
return datas.at(random(datas.size() - 1));
}
}
void generateObject(string& jsonResult, uint32_t& cursor){
while(true){
while(jsonTemplate.at(cursor) != ':'){
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
}
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
while(!isValidChar(cursor)){
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
}
if(jsonTemplate.at(cursor) == '"'){
string values;
++cursor;
while(jsonTemplate.at(cursor) != '"'){
values.push_back(jsonTemplate.at(cursor));
++cursor;
}
++cursor;
jsonResult.append(generateValue(values));
}else if(jsonTemplate.at(cursor) == '['){
uint32_t cur = cursor + 1;
if(isValidChar(cur)){
++cursor;
vector<string> datas;
string data;
while(jsonTemplate.at(cursor) != ']'){
if(jsonTemplate.at(cursor) != ','){
data.push_back(jsonTemplate.at(cursor));
}else{
datas.push_back(data);
data.clear();
}
++cursor;
}
++cursor;
datas.push_back(data);
jsonResult.append(datas.at(random(datas.size() - 1)));
}else{
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
generateList(jsonResult, cursor);
}
}else{
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
generateObject(jsonResult, cursor);
}
while(!isValidChar(cursor)){
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
}
if(jsonTemplate.at(cursor) == '}'){
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
break;
}
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
}
}
void generateList(string& jsonResult, uint32_t& cursor){
while(jsonTemplate.at(cursor) != '[') ++cursor;
++cursor;
string inte;
while(jsonTemplate.at(cursor) != ']'){
inte.push_back(jsonTemplate.at(cursor));
++cursor;
}
int listCount = atoi(inte.data());
while(jsonTemplate.at(cursor) != ',') ++cursor;
++cursor;
int listCursor = cursor;
while(listCount){
cursor = listCursor;
while(!isValidChar(cursor)){
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
}
if(jsonTemplate.at(cursor) == '{'){
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
generateObject(jsonResult, cursor);
}else if(jsonTemplate.at(cursor) == '['){
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
generateList(jsonResult, cursor);
}else{
string values;
++cursor;
while(jsonTemplate.at(cursor) != '"'){
values.push_back(jsonTemplate.at(cursor));
++cursor;
}
++cursor;
jsonResult.append(generateValue(values));
}
--listCount;
if(listCount > 0){
jsonResult.push_back(',');
}
}
while(jsonTemplate.at(cursor) != ']'){
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
}
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
}
string generate(){
uint32_t cursor = 0;
string jsonResult;
while(!isValidChar(cursor)){
jsonResult.push_back(jsonTemplate.at(cursor));
++cursor;
}
jsonResult.push_back(jsonTemplate.at(cursor));
if(jsonTemplate.at(cursor) == '{'){
++cursor;
generateObject(jsonResult, cursor);
}else{
++cursor;
generateList(jsonResult, cursor);
}
for(; cursor < jsonTemplate.size(); ++cursor){
jsonResult.push_back(jsonTemplate.at(cursor));
}
return jsonResult;
}
bool isValidChar(uint32_t& cursor){
return (jsonTemplate.at(cursor) > ' ' && jsonTemplate.at(cursor) < 127);
}
void save(const string& filepath){
std::ofstream ostr(filepath);
string data = generate();
ostr.write(data.data(), data.size());
}
uint32_t random(uint32_t range){
return std::uniform_int_distribution<unsigned>(0, range)(engine);
}
private:
string jsonTemplate;
std::default_random_engine engine;
};
五、结言
json格式是现代编程中必不可少的一种交流格式,在web开发中更是起着举足轻重的作用,在测试中经常需要用到大量的测试用例,手动编写又非常麻烦,这时通过固定的模板,批量生成大量的json数据,可以大大缩减测试的工作量。
对于模板的解析工作,更好的解决办法是分解成一个个节点,再生成的时候直接通过遍历节点就可以快速生成,但是这种方法需要先对上诉方法就是很好的理解之后才能游刃有余,不然很容易弄错,就像要先学会面向过程,才能更好的理解面向对象。
解题是为了更好的去解决生活中所遇到的各种难题,并且更快更好的摆平。
创作不易,留个赞再走吧!如果对文章内容有任何指正,欢迎评论!