JS用途很广, 当不同语言的程序之间进行通信的时候, JSON也是一种不错的格式。这里简要说说基于POCO C++的JSON实现吧。
1. JSON背景参考网站http://www.json.org/
2. JS的JSON操作建议采用json2.js
3. JSON的C++实现, 在上面的网站上有多种实现, 各有千秋, 这里介绍一种基于POCO C++的实现。
4. POCO C++的背景参考网站http://pocoproject.org/index.html, 个人认为POCO C++是完全免费开源好用的。
5. 这里主要用到POCO的两个类, Var和Token, 这里简要介绍, Var可以理解为JS中的var, 不用去考虑类型, 比如Var x = 10; x = 10.0; x = "Hi"。 Token是按照一种规则定义的标志, 即理解为按照一个规则形成的单元。 如有一个字符串" 1234Hi", 这里可以定义空白符Token, 数字Token和字符串Token
6. 长话短说, 看实现, 这里类名是CJSON, 两个对外方法
static std::string ToString(Poco::Dynamic::Var const& var,
Poco::Dynamic::Var const& indent);
static Poco::Dynamic::Var Parse(std::string const& exp);
ToString是将C++对象转换成JSON格式的字符串, 比如
ToString(1, 0), ToString(true, 0), ToString("Hi", 0), ToString(1.111, 0), ToString(map1, 0), ToString(vector1, 0);
注意第二个参数是控制格式缩进的。
Parse是将JSON字符串转换成C++对象, 比如
Parse("true") == true, Parse("false") == false; Parse("null").IsEmpty(); Parse("1") == 1; Parse("\"Hi\"") == "Hi";
namespace Poco
{
namespace Dynamic
{
typedef std::map
template
class VarHolderImpl
{
public:
typedef std::map
VarHolderImpl(const ContainerType& val)
{
for (ContainerType::const_iterator j = val.begin(),
i = val.end(); j != i; ++j)
{
Var key = j->first;
_val.insert(std::make_pair(key, j->second) );
}
}
VarHolder* clone() const
{
return new VarHolderImpl
}
const std::type_info& type() const
{
return typeid(JsObject);
}
void convert(int& val) const
{
val = PtrToInt(&_val);
}
private:
JsObject _val;
};
template <>
class VarHolderImpl
{
public:
VarHolderImpl(const JsObject& val) : _val(val)
{
}
VarHolder* clone() const
{
return new VarHolderImpl
}
const std::type_info& type() const
{
return typeid(JsObject);
}
void convert(int& val) const
{
val = PtrToInt(&_val);
}
private:
JsObject _val;
};
}
}
class CJSON
{
public:
static std::string ToString(Poco::Dynamic::Var const& var,
Poco::Dynamic::Var const& indent);
static Poco::Dynamic::Var Parse(std::string const& exp);
private:
static std::string ToString(Poco::Dynamic::Var const& var,
std::string& gap, std::string& indent);
static std::string Quote(std::string const& str);
class CUserToken : public Poco::Token
{
public:
Poco::Dynamic::Var const& Var() const { return _var; }
Poco::Token::Class tokenClass() const
{
return Poco::Token::USER_TOKEN;
}
protected:
Poco::Dynamic::Var _var;
};
class CNumberToken : public CUserToken
{
public:
bool start(char c, std::istream& /*istr*/)
{
bool ok = c >= '0' && c <= '9' || '-' == c || '+' == c || '.' == c;
if (ok && c != '+') { _value = c; }
return ok;
}
void finish(std::istream& istr)
{
int c = istr.peek();
for (; c >= '0' && c <= '9';
istr.get(), c = istr.peek() )
{
_value += (char)c;
}
if ('.' == c)
{
_value += (char)c;
for (istr.get(), c = istr.peek(); c >= '0' && c <= '9';
istr.get(), c = istr.peek() )
{
_value += (char)c;
}
}
if ('e' == c || 'E' == c)
{
_value += (char)c;
istr.get();
c = istr.peek();
if ('-' == c || '+' == c) {
_value += (char)c;
istr.get();
c = istr.peek();
}
for (; c >= '0' && c <= '9'; istr.get(), c = istr.peek())
{
_value += (char)c;
}
}
int iVal;
Poco::Int64 i64Val;
unsigned int uiVal;
Poco::UInt64 ui64Val;
double dbVal;
if (Poco::NumberParser::tryParse(_value, iVal) )
{
_var = iVal;
}
else if (Poco::NumberParser::tryParse64(_value, i64Val) )
{
_var = iVal;
}
else if (Poco::NumberParser::tryParseUnsigned(_value, uiVal) )
{
_var = uiVal;
}
else if (Poco::NumberParser::tryParseUnsigned64(_value, ui64Val) )
{
_var = ui64Val;
}
else if (Poco::NumberParser::tryParseFloat(_value, dbVal) )
{
_var = dbVal;
}
else
{
throw Poco::SyntaxException("Bad number");
}
}
};
class CStringToken : public CUserToken
{
public:
bool start(char c, std::istream& /*istr*/)
{
std::string empty;
_value.swap(empty);
return '"' == c;
}
void finish(std::istream& istr)
{
int end = 0;
for (int c = istr.peek(); c != -1 && !end;
istr.get(), c = istr.peek() )
{
if ('"' == c) { end = 1; }
else if ('\\' == c)
{
istr.get();
c = istr.peek();
switch (c)
{
case 'u':
{
std::string hex(4, 0);
istr.get( (char*)hex.c_str(), hex.length() );
unsigned hexN = Poco::NumberParser::parseHex(hex);
_value += std::string( (char*)&hexN,
sizeof(unsigned short) );
break;
}
case '"':
case '\\':
case '/':
{
_value += (char)c;
break;
}
case 'b':
{
_value += '\b';
break;
}
case 'f':
{
_value += '\f';
break;
}
case 'n':
{
_value += '\n';
break;
}
case 'r':
{
_value += '\r';
break;
}
case 't':
{
_value += '\t';
break;
}
default:
{
end = 2;
break;
}
}
}
else
{
_value += (char)c;
}
}
if (2 == end)
{
throw Poco::SyntaxException("Bad string");
}
_var = _value;
}
};
class CWordToken : public CUserToken
{
public:
bool start(char c, std::istream& /*istr*/)
{
bool ok = 't' == c || 'f' == c || 'n' == c;
if (ok) { _value = c; }
return ok;
}
void finish(std::istream& istr)
{
char lead = _value[0];
bool ok = false;
switch (lead)
{
case 't':
{
int c = istr.peek();
ok = 'r' == c;
if (!ok) { break; }
istr.get();
c = istr.peek();
ok = 'u' == c;
if (!ok) { break; }
istr.get();
c = istr.peek();
ok = 'e' == c;
if (!ok) { break; }
istr.get();
break;
}
case 'f':
{
int c = istr.peek();
ok = 'a' == c;
if (!ok) { break; }
istr.get();
c = istr.peek();
ok = 'l' == c;
if (!ok) { break; }
istr.get();
c = istr.peek();
ok = 's' == c;
if (!ok) { break; }
istr.get();
c = istr.peek();
ok = 'e' == c;
if (!ok) { break; }
istr.get();
break;
}
case 'n':
{
int c = istr.peek();
ok = 'u' == c;
if (!ok) { break; }
istr.get();
c = istr.peek();
ok = 'l' == c;
if (!ok) { break; }
istr.get();
c = istr.peek();
ok = 'l' == c;
if (!ok) { break; }
istr.get();
break;
}
default:
{
break;
}
}
if (!ok)
{
throw Poco::SyntaxException("Bad word");
}
switch (lead)
{
case 't':
{
_var = true;
break;
}
case 'f':
{
_var = false;
break;
}
default:
{
_var.empty();
break;
}
}
}
};
class CArrayToken : public CUserToken
{
public:
bool start(char c, std::istream& /*istr*/)
{
return '[' == c;
}
void finish(std::istream& istr)
{
Poco::StreamTokenizer st(istr);
st.addToken(new Poco::WhitespaceToken, true);
st.addToken(new CSeparatorToken);
st.addToken(new CValueToken);
std::vector
bool closed = false;
bool hasError = false;
for (Poco::Token const* j = st.next(); !hasError && !closed;)
{
if (!j->is(Poco::Token::USER_TOKEN) )
{
hasError = true;
continue;
}
CUserToken const* k = (CUserToken const*)j;
vars.push_back(k->Var() );
j = st.next();
if (!j->is(Poco::Token::SEPARATOR_TOKEN) )
{
hasError = true;
continue;
}
if (j->asChar() == ']')
{
closed = true;
}
else
{
j = st.next();
}
}
if (!closed) { hasError = true; }
if (hasError) { throw Poco::SyntaxException("Bad array"); }
_var = vars;
}
};
class CObjectToken : public CUserToken
{
public:
bool start(char c, std::istream& /*istr*/)
{
return '{' == c;
}
void finish(std::istream& istr)
{
Poco::StreamTokenizer st(istr);
st.addToken(new Poco::WhitespaceToken, true);
st.addToken(new CStringToken);
st.addToken(new CSeparatorToken);
st.addToken(new CValueToken);
Poco::Dynamic::JsObject obj;
bool closed = false;
bool hasError = false;
for (Poco::Token const* j = st.next(); !hasError && !closed;)
{
if (!j->is(Poco::Token::USER_TOKEN) )
{
hasError = true;
continue;
}
std::string key = j->asString();
j = st.next();
if (j->asChar() != ':')
{
hasError = true;
continue;
}
j = st.next();
if (!j->is(Poco::Token::USER_TOKEN) )
{
hasError = true;
continue;
}
obj[key] = ( (CUserToken const*)j)->Var();
j = st.next();
if (!j->is(Poco::Token::SEPARATOR_TOKEN) )
{
hasError = true;
continue;
}
if (j->asChar() == '}')
{
closed = true;
}
else
{
j = st.next();
}
}
if (!closed) { hasError = true; }
if (hasError) { throw Poco::SyntaxException("Bad object"); }
_var = obj;
}
};
class CValueToken : public CUserToken
{
public:
bool start(char c, std::istream& /*istr*/)
{
bool ok = c > ' ';
if (ok) { _value = c; }
return ok;
}
void finish(std::istream& istr)
{
char c = _value[0];
istr.putback(c);
Poco::StreamTokenizer st(istr);
switch (c)
{
case '{':
{
st.addToken(new CObjectToken);
break;
}
case '[':
{
st.addToken(new CArrayToken);
break;
}
case '"':
{
st.addToken(new CStringToken);
break;
}
case '-':
case '+':
case '.':
{
st.addToken(new CNumberToken);
break;
}
default:
{
if (c >= '0' && c <= '9')
{
st.addToken(new CNumberToken);
}
else
{
st.addToken(new CWordToken);
}
break;
}
}
Poco::Token const* token = st.next();
if (!token->is(Poco::Token::USER_TOKEN) )
{
throw Poco::SyntaxException("JSON.parse");
}
_var = ( (CUserToken const*)token)->Var();
}
};
class CSeparatorToken : public Poco::Token
{
public:
Poco::Token::Class tokenClass() const { return SEPARATOR_TOKEN; }
bool start(char c, std::istream& /*istr*/)
{
bool ok = ',' == c || ':' == c || ']' == c || '}' == c;
if (ok) { _value = c; }
return ok;
}
};
};
inline
std::string CJSON::ToString(const Poco::Dynamic::Var &var,
const Poco::Dynamic::Var &indent)
{
std::string gap, indentP;
int indentVal;
if (Poco::NumberParser::tryParse(indent, indentVal) )
{
for (int j = 0; j < indentVal; ++j)
{
indentP += ' ';
}
}
else if (indent.isString() )
{
indentP = (std::string)indent;
}
return ToString(var, gap, indentP);
}
inline
std::string CJSON::ToString(const Poco::Dynamic::Var &var, std::string& gap,
std::string& indent)
{
std::string result;
std::string mind = gap;
if (var.isEmpty() )
{
result = "null";
}
else if (var.isArray() )
{
gap += indent;
std::vector
for (int j = 0, hasError = 0; !hasError; ++j)
{
try
{
partial.push_back(ToString(var[j], gap, indent) );
}
catch (Poco::RuntimeException&)
{
hasError = 1;
}
}
result = partial.empty() ? "[]" : (!gap.empty() ?
("[\n" + gap + CUtils::Join(partial, ",\n" + gap) + "\n" + mind +
"]") : ("[" + CUtils::Join(partial, ",") + "]" ) );
gap = mind;
}
else if (var.type() == typeid(Poco::Dynamic::JsObject) )
{
gap += indent;
std::vector
int addr = var;
Poco::Dynamic::JsObject *pJsObject =
(Poco::Dynamic::JsObject*)IntToPtr(addr);
for (Poco::Dynamic::JsObject::const_iterator j = pJsObject->begin(),
i = pJsObject->end(); j != i; ++j)
{
std::string tmp = ToString(j->second, gap, indent);
partial.push_back(Quote(j->first) +
(!gap.empty() ? ": " : ":") + tmp);
}
result = partial.empty() ? "{}" : (!gap.empty() ?
("{\n" + gap + CUtils::Join(partial, ",\n" + gap) + "\n" + mind +
"}") : ("{" + CUtils::Join(partial, ",") + "}" ) );
gap = mind;
}
else if (var.isString() )
{
result = Quote(var);
}
else
{
result = (std::string)var;
}
return result;
}
inline
std::string CJSON::Quote(const std::string &utf8)
{
char const* escapable = "[\\\\\\\"\\x00-\\x1f\\x7f-\\x9f\\x{00ad}"
"\\x{0600}-\\x{0604}\\x{070f}\\x{17b4}\\x{17b5}\\x{200c}-\\x{200f}"
"\\x{2028}-\\x{202f}\\x{2060}-\\x{206f}\\x{feff}\\x{fff0}-\\x{ffff}]";
if (!CUtils::IsValidUTF8(utf8.c_str() ) )
{
throw Poco::InvalidArgumentException(utf8 + " is not UTF encoded");
}
std::string result = "\"";
std::string copy = utf8;
Poco::RegularExpression reg(escapable, Poco::RegularExpression::RE_UTF8);
Poco::RegularExpression::Match m;
int test = reg.match(utf8, 0, m);
for (size_t offset = 0; test != 0;
test = reg.match(utf8, m.offset + m.length, m) )
{
size_t pos = m.offset + offset;
std::string sub;
char& ch = copy[pos];
switch (ch)
{
case '\\':
case '\b':
case '\t':
case '\n':
case '\f':
case '\r':
case '"':
{
sub += '\\';
sub += ch;
break;
}
default:
{
unsigned short tmp = 2 == m.length ? *( (unsigned short*)&ch) :
ch;
sub = Poco::format("%04hX", tmp);
break;
}
}
copy.replace(pos, m.length, sub);
offset += sub.length() - m.length;
}
result += copy;
result += "\"";
return result;
}
inline
Poco::Dynamic::Var CJSON::Parse(const std::string &exp)
{
if (!CUtils::IsValidUTF8(exp.c_str() ) )
{
throw Poco::InvalidArgumentException(exp + " is not UTF encoded");
}
std::stringstream ss(exp);
Poco::StreamTokenizer st(ss);
st.addToken(new Poco::WhitespaceToken, true);
st.addToken(new CValueToken);
Poco::Token const* token = st.next();
if (!token->is(Poco::Token::USER_TOKEN) )
{
throw Poco::SyntaxException("JSON.parse");
}
return ( (CUserToken const*)token)->Var();
}