问题
I've been using Casablanca Json C++ library (cpprest) successfully for some time. Its parser (web::json::value::parse(<json_string>)
) works perfectly on valid JSON strings. Say this will be parsed correctly:
{
"key1": [["1", 0.4], ["0", 0.6]],
"key2": true,
"key3": 1,
"key4": [{"key41": 1}, {"key42": [1,2,3]}]
}
Now, I faced the necessity of parsing JSON objects, the keys of which are not enclosed into double quotes:
{
key1: [[1, 0.4], [0, 0.6]],
key2: true,
key3: 1,
key4: [{key41: 1}, {key42: [1,2,3]}]
}
Is there a nice way to correctly parse this and then serialize into a valid JSON, so that Casablanca can parse the resultant valid JSON correctly?
Hjson seems to work for this purpose, but it doesn't provide the required library for C++. They mention jzon
library for C - I tried it: it has only one-way parsing (no serialization), and even parsing doesn't work correctly (can't even parse valid JSONs)
回答1:
This probably won't be the fastest way to do this, but if niceness is measured in least lines of code, it'll come up pretty high.
What you have is a javascript-like object. Let's plug it into a javascript engine and use it to spit out proper JSON. I'll use Qt's QJSEngine
since I'm passably familiar with it:
constexpr char const* str = R"({
key1: [[1, 0.4], [0, 0.6]],
key2: true,
key3: 1,
key4: [{key41: 1}, {key42: [1,2,3]}]
})";
QJSEngine e;
QString script = QString("JSON.stringify(%0)").arg(str);
then you can just evaluate it:
e.evaluate(script).toString().toStdString()
yields
{"key1":[[1,0.4],[0,0.6]],"key2":true,"key3":1,"key4":[{"key41":1},{"key42":[1,2,3]}]}
回答2:
This crude method will work.
(Untested code )
We've got three states, NEUTRAL, ONSTRING, and ONALNUM.
We start in NEUTRAL. If we hit '"' we go into ONSTRING. If we hit alpha we go into ONALNUM. If we go into or out of alnum, we emit a quote. We also emit the character read. If we are in ONALNUM, we get out of it when we hit non-alnum, amd go into NEUTRAL, unless we hit quote, when it's a parse error. If we are in ONSTRING we apply JSON string escape rules, which I don't know offhand.
#define NEUTRAL 0
#define ONSTRING 1
#define ONALNUM 2
int state = NEUTRAL;
char *inptr = str;
char ch;
while( (ch = *inptr++))
{
if(state == NEUTRAL)
{
if( isalpha(ch) )
{
emit('\"');
state = ONALNUM;
}
else if(ch = '\"')
state = ONSTRING;
emit(ch);
}
else if(state == ONSTRING)
{
/* JSON string escape rules here */
if(ch == '\"')
state = NEUTRAL;
emit(ch);
}
else if(state == ONALNUM)
{
emit(ch);
if(!isalnum(ch))
{
state = NEUTRAL;
emit('\"');
}
}
}
回答3:
Problem
It seems by the context of your question that you want the "" removed so you can have a proper JSON format.
For the JSON parsing part, you should use a library. I'm going to post an example of it here, soon.
Solution
In order to do that, we are going to use the std::replace
function from the <algorithm>
library; although we can implement this on our own, it is better to use standard libraries since the creators of them worked hard on optimizing those function to the fullest capabilities. So let's take your code you gave us from the question and make it JSON-appropriate.
#include <algorithm>
#include <string>
#include <iostream>
using std::string;
using std::cout;
using std::endl;
void convert_char(string &s,char from_conv, char to_conv) {
std::replace( s.begin(), s.end(), from_conv, to_conv); // replace all 'x' to 'y'
}
int main()
{
string str = "{ \n \
\"key1\": [[\"1\", 0.4], [\"0\", 0.6]], \n \
\"key2\": true, \n \
\"key3\": 1, \n \
\"key4\": [{\"key41\": 1}, {\"key42\": [1,2,3]}] \n }";;
convert_char(str,'\"',(char)0);
cout << str << endl;
}
You can see here we have a function called convert_char
which converts a certain character to another. So basically as your question asked we removed the double quotation, and tada, it is formatted like JSON! Take a look here for the demo.
Solution to JSON Parser
Obviously, here you will use a library to do this for you. I'm going to introduce sciter
to you! Basically, with sciter
all you got to do is:
#include <algorithm>
#include <string>
#include <iostream>
#include <sciter>
using std::string;
using std::cout;
using std::endl;
int main()
{
string str = "{ \n \
\"key1\": [[\"1\", 0.4], [\"0\", 0.6]], \n \
\"key2\": true, \n \
\"key3\": 1, \n \
\"key4\": [{\"key41\": 1}, {\"key42\": [1,2,3]}] \n }";;
sciter::value str_conv = sciter::value::from_string( str, CVT_JSON_LITERAL );
cout << str_conv << endl;
}
Now according to this code, the JSON Formatted code is in str_conv
! References to guide you through this are below in the References section.
References:
sciter
cpprefrence std::replace
string::replace
cpprefrence std::refrence_if
Glossary
std::replace
:
Prototype:
template <class ForwardIterator, class T>
void replace (ForwardIterator first, ForwardIterator last,
const T& old_value, const T& new_value); //source cpprefrence
references: cpprefrence
<algorithm>
:
Many topics are inside the algorithm library. It's a library for, you guessed it, algorithms.
The header <algorithm>
defines a collection of functions especially designed to be used on ranges of elements.
A range is any sequence of objects that can be accessed through iterators or pointers, such as an array or an instance of some of the STL containers. Notice though, that
algorithms
operate throughiterators
directly on the values, not affecting in any way the structure of any possible container (it never affects the size or storage allocation of the container).The algorithms library defines functions for a variety of purposes (e.g. searching, sorting, counting, manipulating) that operate on ranges of elements.
References:
cplusplus
cpprefrence
来源:https://stackoverflow.com/questions/39928124/c-parsing-json-string-having-keys-not-enclosed-into-double-quotes