JSON查询 201709-3 C++

文章描述了一个实现JSON查询器的程序,该程序能解析JSON字符串为map数据结构,并根据查询字符串查找对应的值。解析过程通过递归函数parseObject完成,处理JSON中的对象、键值对和字符串。主函数部分读取多个JSON字符串和查询字符串,输出查询结果。如果查询的路径对应值为空,则输出OBJECT,表示是未填充的子对象;否则输出STRING及对应的值。
摘要由CSDN通过智能技术生成


一、题目

在这里插入图片描述
在这里插入图片描述

原题目链接

二、解题

1.题目

实现一个JSON查询器。它读取一个JSON格式的字符串,解析为一个map<string, string>的字典,键为JSON中的路径,值为JSON中对应路径的值。然后它读取一些查询字符串,并根据查询字符串查找对应的值并输出。

程序的实现分为三个部分:

  1. 解析JSON字符串部分
    这部分代码主要实现了解析JSON格式的字符串并将其解析为一个键值对的字典。首先调用parseObject函数,该函数接收JSON字符串,一个前缀(表示JSON中各层级的路径),一个引用的空字典,以及一个int型变量i(表示位置)的引用作为参数。parseObject函数主要采用递归的方式实现对JSON字符串的解析,具体步骤如下:

(1) 如果当前位置的字符是左括号,则说明该位置代表一个对象(object),idx加1之后进行下一步解析。

(2) 首先设置一个标识符strType,该变量初始值为false,表示当前解析的是键,而不是值。然后进入循环,按照从左到右依次解析每个键值对:

  • 如果当前位置的字符是双引号,则说明后面跟着的是一个字符串,需要调用parseString函数解析出该字符串的值。
  • 如果strType为false,则说明当前解析的是键,需要把当前解析出的字符串累加到前缀上,并设置strType为true表示后面需要解析值。
  • 如果strType为true,则说明当前解析的是值,需要把当前解析出的字符串作为值存入字典中,并把strType重新置为false,表示需要解析下一个键。
  • 如果当前位置的字符是逗号,则说明后面还有其他的键值对,需要把strType置为false,表示需要解析下一个键。循环接着进行下一次迭代。
  • 如果当前位置的字符是左括号,则说明后面跟着的是一个新的对象(object),需要在字典中为当前键设置一个空值,并递归地调用parseObject函数解析这个新的对象。
  • 如果当前位置的字符是右括号,则说明当前的对象解析完成,跳出循环。

(3) 如果当前位置的字符是右括号,则说明当前的对象解析完成,函数结束。

  1. 读取查询字符串部分
    这部分代码通过循环读取查询字符串,并调用map的find方法查找该查询字符串是否存在于解析后的字典中。如果不存在,则输出NOTEXIST;如果存在,则分两种情况输出结果:
  • 如果该查询字符串在字典中对应的值为空,说明该字符串是一个路径,但不是叶子节点,输出OBJECT;
  • 如果该查询字符串在字典中对应的值不为空,说明该字符串是一个叶子节点的路径,输出STRING和对应的值。
  1. 主函数部分
    这部分代码主要是读入N和M的值,以及读取N行输入的JSON字符串,调用parseObject函数解析JSON字符串,并读取M个查询字符串,调用map的find方法查找每个查询字符串在字典中的值并输出。

2.代码

dev c++ 5.11

#include<iostream>
#include<map>
#include<cassert>
using namespace std;


string parseString(string &str,int &idx){
	string tmp;
	if(str[idx]=='"') idx++;
	else assert(0);
	
	while(idx<str.size()){
		if(str[idx]=='\\' ){
			idx++;
			tmp+=str[idx];
			idx++;
		}
		else if(str[idx]=='"'){
			break;
		}
		else{
			tmp+=str[idx];
			idx++;
		}
	}
	if(str[idx]=='"') idx++;
	else assert(0);
	return tmp;
}



void  parseObject(string &str,string prefix, map<string,string> &dict,int &idx){
	if(str[idx]=='{') idx++;
	else assert(0);
	
	string key,value;
	bool strType=false;//false对应key,true对应value 
	while(idx<str.size()){
		if(str[idx]=='"'){//遇到:,解析key-value 
			string tmp=parseString(str,idx);
			if(strType){
				value=tmp;
				dict[key]=value;
			}
			else{
				key=prefix+(prefix==""?"":".") + tmp;
			}
		}else if(str[idx]==':'){//遇到:,说明现在开始解析value 
			strType=true;
			idx++;
		}else if(str[idx]==','){//遇到,,说明value解析完成,开始解析下一个key 
			strType=false;
			idx++;
		}else if(str[idx]=='{'){//遇到{,就是解析子对象的情况,将子对象的key设置为空字符串 
			dict[key]="";
			parseObject(str,key,dict,idx);//递归解析 
		}else if(str[idx]=='}'){
			break;
		}else{
			idx++;
		} 
	}
	if(str[idx]=='}') idx++;
	else assert(0);
	
}



int main(){
	int N,M;
	cin>>N>>M;
	string json;
	if(cin.peek()=='\n') cin.ignore();//清除回车 
	for(int n=0;n<N;n++){
		string tmp;
		getline(cin,tmp);
		json+=tmp;
	}
	map<string,string> dict;
	int idx=0;
	parseObject(json,"",dict ,idx);
	string query;
	for(int m=0;m<M;m++){
		getline(cin,query);
		if(dict.find(query)==dict.end()){//查询不到 
			cout<<"NOTEXIST"<<endl;
		}else{
			if(dict[query]==""){
				cout<<"OBJECT"<<endl;
			}else {
				cout<<"STRING "<<dict[query]<<endl;
			}
		}
	}
	
}



3.提交结果

在这里插入图片描述

总结

1.解释

  1. 输入数据
if(cin.peek()=='\n') cin.ignore();//清除回车 
	for(int n=0;n<N;n++){
		string tmp;
		getline(cin,line);
		json+=tmp;
	}

首先,if(cin.peek()==‘n’) cin.ignore(); 的作用是在输入完 N 后,忽略掉一个回车符。因为在输入完 N 之后,按下回车键会把回车符n留在输入流中,如果不忽略掉这个回车符,下一行输入 getline(cin, tmp) 就会读取到这个回车符,导致接下来的输入有问题。

其次,for(int n=0; n<N; n++) 循环用于读取 N 行 JSON 文本,并将读取到的每一行文本都附加到 json 变量末尾,以便之后进行解析。getline(cin, tmp) 意思是将 cin 流中的一行输入读入到 tmp 变量中,这里一行输入对应着一个 JSON 对象。每次读取完一行后,将 tmp 的内容附加到 json 变量末尾。最终,json 变量就包含了输入流中的所有文本。

  1. 函数parseObject(json, "", dict, i) 是对读入的 JSON 进行解析,解析完后将结果存储到 dict 变量中,以便之后查询某个 key 的值。
  • str:表示待解析的JSON字符串;
  • prefix:表示当前对象的前缀,即此对象在整个JSON串中的完整路径,如"a.b.c";
  • dict:表示解析得到的键值对,是一个map类型,用来存储JSON对象中的所有键值对;
  • i:表示当前解析到的字符在JSON字符串中的下标位置。
  1. dict[key] = "";
    这个程序中,dict[key] = “”; 的意思是将一个空字符串作为当前父对象(prefix)下key所代表的子对象({})的值存储到了map中,以便在后续的查询(if语句中dict[query]==“”)时能够识别当前的查询对象是否是一个空的子对象。

举例说明:假设有JSON字符串{“person”:{“name”:“Alice”,“address”:{}}},将该JSON字符串解析后存储到map中,其中prefix为空字符串,解析开始时dict为空。遇到"person"时,key=“person”,strType=false,idx向后移动。遇到"name"时,key=“person.name”,strType=false,idx向后移动。遇到"Alice"时,value=“Alice”,strType=true,将"person.name"和"Alice"存储到dict中。遇到"address"时,key=“person.address”,strType=false, dict[key]=“”;将一个空串作为当前父对象下"address"所代表的子对象的值存入dict中,再递归解析该空对象。因为该子对象为空,解析该子对象时会将dict[“person.address”]设置为空串。
这样,当查询"person.address"时,由于dict[“person.address”]==“”,所以程序会输出OBJECT来表示查询对象是一个空子对象,否则将输出查询对象的字符串值。

  1. key=prefix+(prefix==""?"":".") + tmp;
    这句代码是将解析得到的key值与一个前缀字符串 prefix 拼接起来,得到完整的 key 值,用于存储到 map 容器(字典)。它前面的条件语句 (prefix==“”?“”:“.”) 的作用是在非空的前缀字符串前加上一个点号,表示当前 key 值是由前缀字符串及当前字符串组成。举个例子:

如果当前的 key 是 “age”,前缀字符串是 “person”,那么拼接起来的完整 key 就是 “person.age”。

如果当前的 key 是 “name”,没有前缀字符串,那么拼接起来的完整 key 就是 “name”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值