|
@@ -0,0 +1,612 @@
|
|
|
+/* <!-- copyright */
|
|
|
+/*
|
|
|
+ * aria2 - The high speed download utility
|
|
|
+ *
|
|
|
+ * Copyright (C) 2011 Tatsuhiro Tsujikawa
|
|
|
+ *
|
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
|
+ * (at your option) any later version.
|
|
|
+ *
|
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ * GNU General Public License for more details.
|
|
|
+ *
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
+ * along with this program; if not, write to the Free Software
|
|
|
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
+ *
|
|
|
+ * In addition, as a special exception, the copyright holders give
|
|
|
+ * permission to link the code of portions of this program with the
|
|
|
+ * OpenSSL library under certain conditions as described in each
|
|
|
+ * individual source file, and distribute linked combinations
|
|
|
+ * including the two.
|
|
|
+ * You must obey the GNU General Public License in all respects
|
|
|
+ * for all of the code used other than OpenSSL. If you modify
|
|
|
+ * file(s) with this exception, you may extend this exception to your
|
|
|
+ * version of the file(s), but you are not obligated to do so. If you
|
|
|
+ * do not wish to do so, delete this exception statement from your
|
|
|
+ * version. If you delete this exception statement from all source
|
|
|
+ * files in the program, then also delete it here.
|
|
|
+ */
|
|
|
+/* copyright --> */
|
|
|
+#include "json.h"
|
|
|
+
|
|
|
+#include <sstream>
|
|
|
+
|
|
|
+#include "array_fun.h"
|
|
|
+#include "DlAbortEx.h"
|
|
|
+#include "error_code.h"
|
|
|
+#include "a2functional.h"
|
|
|
+#include "util.h"
|
|
|
+#include "fmt.h"
|
|
|
+
|
|
|
+namespace aria2 {
|
|
|
+
|
|
|
+namespace json {
|
|
|
+
|
|
|
+// Function prototype declaration
|
|
|
+namespace {
|
|
|
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+decode
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last,
|
|
|
+ size_t depth);
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+const char WS[] = { 0x20, 0x09, 0x0a, 0x0d };
|
|
|
+const char ESCAPE_CHARS[] = { '"', '\\', '/', '\b', '\f', '\n', '\r', '\t' };
|
|
|
+const size_t MAX_STRUCTURE_DEPTH = 100;
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+std::string::const_iterator skipWs
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last)
|
|
|
+{
|
|
|
+ while(first != last && std::find(vbegin(WS), vend(WS), *first) != vend(WS)) {
|
|
|
+ ++first;
|
|
|
+ }
|
|
|
+ return first;
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+void checkEof
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last)
|
|
|
+{
|
|
|
+ if(first == last) {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed: unexpected EOF",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+std::string::const_iterator
|
|
|
+decodeKeyword
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last,
|
|
|
+ const std::string& keyword)
|
|
|
+{
|
|
|
+ size_t len = keyword.size();
|
|
|
+ for(size_t i = 0; i < len; ++i) {
|
|
|
+ checkEof(first, last);
|
|
|
+ if(*first != keyword[i]) {
|
|
|
+ throw DL_ABORT_EX2(fmt("JSON decoding failed: %s not found.",
|
|
|
+ keyword.c_str()),
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+ ++first;
|
|
|
+ }
|
|
|
+ return first;
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+decodeTrue
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last)
|
|
|
+{
|
|
|
+ first = decodeKeyword(first, last, "true");
|
|
|
+ return std::make_pair(Bool::gTrue(), first);
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+decodeFalse
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last)
|
|
|
+{
|
|
|
+ first = decodeKeyword(first, last, "false");
|
|
|
+ return std::make_pair(Bool::gFalse(), first);
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+decodeNull
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last)
|
|
|
+{
|
|
|
+ first = decodeKeyword(first, last, "null");
|
|
|
+ return std::make_pair(Null::g(), first);
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+decodeString
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last)
|
|
|
+{
|
|
|
+ // Consume first char, assuming it is '"'.
|
|
|
+ ++first;
|
|
|
+ std::string s;
|
|
|
+ std::string::const_iterator offset = first;
|
|
|
+ while(first != last) {
|
|
|
+ if(*first == '"') {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if(*first == '\\') {
|
|
|
+ s += std::string(offset, first);
|
|
|
+ ++first;
|
|
|
+ checkEof(first, last);
|
|
|
+ if(*first == 'u') {
|
|
|
+ ++first;
|
|
|
+ std::string uchars;
|
|
|
+ for(int i = 0; i < 4; ++i, ++first) {
|
|
|
+ checkEof(first, last);
|
|
|
+ uchars += *first;
|
|
|
+ }
|
|
|
+ checkEof(first, last);
|
|
|
+ uint16_t codepoint = util::parseUInt(uchars, 16);
|
|
|
+ if(codepoint <= 0x007fu) {
|
|
|
+ s += static_cast<char>(codepoint);
|
|
|
+ } else if(codepoint <= 0x07ffu) {
|
|
|
+ unsigned char c2 = 0x80u | (codepoint & 0x003fu);
|
|
|
+ unsigned char c1 = 0xC0u | (codepoint >> 6);
|
|
|
+ s += c1;
|
|
|
+ s += c2;
|
|
|
+ } else if(in(codepoint, 0xD800u, 0xDBFFu)) {
|
|
|
+ // surrogate pair
|
|
|
+ if(*first != '\\' || first+1 == last ||
|
|
|
+ *(first+1) != 'u') {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed: bad UTF-8 sequence.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+ first += 2;
|
|
|
+ std::string uchars;
|
|
|
+ for(int i = 0; i < 4; ++i, ++first) {
|
|
|
+ checkEof(first, last);
|
|
|
+ uchars += *first;
|
|
|
+ }
|
|
|
+ checkEof(first, last);
|
|
|
+ uint16_t codepoint2 = util::parseUInt(uchars, 16);
|
|
|
+ if(!in(codepoint2, 0xDC00u, 0xDFFFu)) {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed: bad UTF-8 sequence.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+ uint32_t fullcodepoint = 0x010000u;
|
|
|
+ fullcodepoint += (codepoint & 0x03FFu) << 10;
|
|
|
+ fullcodepoint += (codepoint2 & 0x03FFu);
|
|
|
+ unsigned char c4 = 0x80u | (fullcodepoint & 0x003Fu);
|
|
|
+ unsigned char c3 = 0x80u | ((fullcodepoint >> 6) & 0x003Fu);
|
|
|
+ unsigned char c2 = 0x80u | ((fullcodepoint >> 12) & 0x003Fu);
|
|
|
+ unsigned char c1 = 0xf0u | (fullcodepoint >> 18);
|
|
|
+ s += c1;
|
|
|
+ s += c2;
|
|
|
+ s += c3;
|
|
|
+ s += c4;
|
|
|
+ } else if(codepoint <= 0xffffu) {
|
|
|
+ unsigned char c3 = 0x80u | (codepoint & 0x003Fu);
|
|
|
+ unsigned char c2 = 0x80u | ((codepoint >> 6) & 0x003Fu);
|
|
|
+ unsigned char c1 = 0xE0u | (codepoint >> 12);
|
|
|
+ s += c1;
|
|
|
+ s += c2;
|
|
|
+ s += c3;
|
|
|
+ }
|
|
|
+ offset = first;
|
|
|
+ } else {
|
|
|
+ if(*first == 'b') {
|
|
|
+ s += '\b';
|
|
|
+ } else if(*first == 'f') {
|
|
|
+ s += '\f';
|
|
|
+ } else if(*first == 'n') {
|
|
|
+ s += '\n';
|
|
|
+ } else if(*first == 'r') {
|
|
|
+ s += '\r';
|
|
|
+ } else if(*first == 't') {
|
|
|
+ s += '\t';
|
|
|
+ } else {
|
|
|
+ s += *first;
|
|
|
+ }
|
|
|
+ ++first;
|
|
|
+ offset = first;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ++first;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ checkEof(first, last);
|
|
|
+ if(std::distance(offset, first) > 0) {
|
|
|
+ s += std::string(offset, first);
|
|
|
+ }
|
|
|
+ if(!util::isUtf8(s)) {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed: Non UTF-8 string.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+ ++first;
|
|
|
+ return std::make_pair(String::g(s), first);
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+void checkEmptyDigit
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last)
|
|
|
+{
|
|
|
+ if(std::distance(first, last) == 0) {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed: zero DIGIT.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+void checkLeadingZero
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last)
|
|
|
+{
|
|
|
+ if(std::distance(first, last) > 2 && *first == '0') {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed: leading zero.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+decodeNumber
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last)
|
|
|
+{
|
|
|
+ std::string s;
|
|
|
+ if(*first == '-') {
|
|
|
+ s += *first;
|
|
|
+ ++first;
|
|
|
+ }
|
|
|
+ std::string::const_iterator offset = first;
|
|
|
+ while(first != last && in(*first, '0', '9')) {
|
|
|
+ ++first;
|
|
|
+ }
|
|
|
+ checkEof(first, last);
|
|
|
+ checkEmptyDigit(offset, first);
|
|
|
+ checkLeadingZero(offset, first);
|
|
|
+ s += std::string(offset, first);
|
|
|
+ bool fp = false;
|
|
|
+ if(*first == '.') {
|
|
|
+ fp = true;
|
|
|
+ s += *first;
|
|
|
+ ++first;
|
|
|
+ offset = first;
|
|
|
+ while(first != last && in(*first, '0', '9')) {
|
|
|
+ ++first;
|
|
|
+ }
|
|
|
+ checkEof(first, last);
|
|
|
+ checkEmptyDigit(offset, first);
|
|
|
+ s += std::string(offset, first);
|
|
|
+ }
|
|
|
+ if(*first == 'e') {
|
|
|
+ fp = true;
|
|
|
+ s += *first;
|
|
|
+ ++first;
|
|
|
+ checkEof(first, last);
|
|
|
+ if(*first == '+' || *first == '-') {
|
|
|
+ s += *first;
|
|
|
+ ++first;
|
|
|
+ }
|
|
|
+ offset = first;
|
|
|
+ while(first != last && in(*first, '0', '9')) {
|
|
|
+ ++first;
|
|
|
+ }
|
|
|
+ checkEof(first, last);
|
|
|
+ checkEmptyDigit(offset, first);
|
|
|
+ s += std::string(offset, first);
|
|
|
+ }
|
|
|
+ if(fp) {
|
|
|
+ // Since we don't have floating point coutner part in ValueBase,
|
|
|
+ // we just treat it as string.
|
|
|
+ return std::make_pair(String::g(s), first);
|
|
|
+ } else {
|
|
|
+ Integer::ValueType val = util::parseLLInt(s);
|
|
|
+ return std::make_pair(Integer::g(val), first);
|
|
|
+ }
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+void checkDepth(size_t depth)
|
|
|
+{
|
|
|
+ if(depth >= MAX_STRUCTURE_DEPTH) {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed: Structure is too deep.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+decodeArray
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last,
|
|
|
+ size_t depth)
|
|
|
+{
|
|
|
+ checkDepth(depth);
|
|
|
+ SharedHandle<List> list = List::g();
|
|
|
+ // Consume first char, assuming it is '['.
|
|
|
+ ++first;
|
|
|
+ first = skipWs(first, last);
|
|
|
+ checkEof(first, last);
|
|
|
+ if(*first != ']') {
|
|
|
+ while(1) {
|
|
|
+ std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+ r = decode(first, last, depth);
|
|
|
+ list->append(r.first);
|
|
|
+ first = r.second;
|
|
|
+ first = skipWs(first, last);
|
|
|
+ if(first == last || (*first != ',' && *first != ']')) {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed:"
|
|
|
+ " value-separator ',' or ']' is not found.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+ if(*first == ']') {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ++first;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ++first;
|
|
|
+ return std::make_pair(list, first);
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+decodeObject
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last,
|
|
|
+ size_t depth)
|
|
|
+{
|
|
|
+ checkDepth(depth);
|
|
|
+ SharedHandle<Dict> dict = Dict::g();
|
|
|
+ // Consume first char, assuming it is '{'
|
|
|
+ ++first;
|
|
|
+ first = skipWs(first, last);
|
|
|
+ checkEof(first, last);
|
|
|
+ if(*first != '}') {
|
|
|
+ while(1) {
|
|
|
+ std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+ keyRet = decodeString(first, last);
|
|
|
+ first = keyRet.second;
|
|
|
+ first = skipWs(first, last);
|
|
|
+ if(first == last || *first != ':') {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed:"
|
|
|
+ " name-separator ':' is not found.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+ ++first;
|
|
|
+ std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+ valueRet = decode(first, last, depth);
|
|
|
+ dict->put(asString(keyRet.first)->s(), valueRet.first);
|
|
|
+ first = valueRet.second;
|
|
|
+ first = skipWs(first, last);
|
|
|
+ if(first == last || (*first != ',' && *first != '}')) {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed:"
|
|
|
+ " value-separator ',' or '}' is not found.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+ if(*first == '}') {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ++first;
|
|
|
+ first = skipWs(first, last);
|
|
|
+ checkEof(first, last);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ++first;
|
|
|
+ return std::make_pair(dict, first);
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+namespace {
|
|
|
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
|
|
|
+decode
|
|
|
+(std::string::const_iterator first,
|
|
|
+ std::string::const_iterator last,
|
|
|
+ size_t depth)
|
|
|
+{
|
|
|
+ first = skipWs(first, last);
|
|
|
+ if(first == last) {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed:"
|
|
|
+ " Unexpected EOF in term context.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+ if(*first == '[') {
|
|
|
+ return decodeArray(first, last, depth+1);
|
|
|
+ } else if(*first == '{') {
|
|
|
+ return decodeObject(first, last, depth+1);
|
|
|
+ } else if(*first == '"') {
|
|
|
+ return decodeString(first, last);
|
|
|
+ } else if(*first == '-' || in(*first, '0', '9')) {
|
|
|
+ return decodeNumber(first, last);
|
|
|
+ } else if(*first == 't') {
|
|
|
+ return decodeTrue(first, last);
|
|
|
+ } else if(*first == 'f') {
|
|
|
+ return decodeFalse(first, last);
|
|
|
+ } else if(*first == 'n') {
|
|
|
+ return decodeNull(first, last);
|
|
|
+ } else {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed:"
|
|
|
+ " Unexpected EOF in term context.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+
|
|
|
+SharedHandle<ValueBase> decode(const std::string& json)
|
|
|
+{
|
|
|
+ std::string::const_iterator first = json.begin();
|
|
|
+ std::string::const_iterator last = json.end();
|
|
|
+ first = skipWs(first, last);
|
|
|
+ if(first == last) {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed:"
|
|
|
+ " Unexpected EOF in term context.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+ std::pair<SharedHandle<ValueBase>, std::string::const_iterator> r;
|
|
|
+ if(*first == '[') {
|
|
|
+ r = decodeArray(first, last, 1);
|
|
|
+ } else if(*first == '{') {
|
|
|
+ r = decodeObject(first, last, 1);
|
|
|
+ } else {
|
|
|
+ throw DL_ABORT_EX2("JSON decoding failed:"
|
|
|
+ " Unexpected EOF in term context.",
|
|
|
+ error_code::JSON_PARSE_ERROR);
|
|
|
+ }
|
|
|
+ return r.first;
|
|
|
+}
|
|
|
+
|
|
|
+std::string jsonEscape(const std::string& s)
|
|
|
+{
|
|
|
+ std::string t;
|
|
|
+ for(std::string::const_iterator i = s.begin(), eoi = s.end(); i != eoi;
|
|
|
+ ++i) {
|
|
|
+ if(*i == '"' || *i == '\\' || *i == '/') {
|
|
|
+ t += '\\';
|
|
|
+ t += *i;
|
|
|
+ } else if(*i == '\b') {
|
|
|
+ t += "\\b";
|
|
|
+ } else if(*i == '\f') {
|
|
|
+ t += "\\f";
|
|
|
+ } else if(*i == '\n') {
|
|
|
+ t += "\\n";
|
|
|
+ } else if(*i == '\r') {
|
|
|
+ t += "\\r";
|
|
|
+ } else if(*i == '\t') {
|
|
|
+ t += "\\t";
|
|
|
+ } else if(in(static_cast<unsigned char>(*i), 0x00u, 0x1Fu)) {
|
|
|
+ t += "\\u00";
|
|
|
+ char temp[3];
|
|
|
+ temp[2] = '\0';
|
|
|
+ temp[0] = (*i >> 4);
|
|
|
+ temp[1] = (*i)&0x0Fu;
|
|
|
+ for(int j = 0; j < 2; ++j) {
|
|
|
+ if(temp[j] < 10) {
|
|
|
+ temp[j] += '0';
|
|
|
+ } else {
|
|
|
+ temp[j] += 'A'-10;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ t += temp;
|
|
|
+ } else {
|
|
|
+ t += *i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return t;
|
|
|
+}
|
|
|
+
|
|
|
+std::string encode(const ValueBase* vlb)
|
|
|
+{
|
|
|
+ class JsonValueBaseVisitor:public ValueBaseVisitor {
|
|
|
+ private:
|
|
|
+ std::ostringstream out_;
|
|
|
+ public:
|
|
|
+ virtual void visit(const String& string)
|
|
|
+ {
|
|
|
+ const std::string& s = string.s();
|
|
|
+ std::string t = jsonEscape(s);
|
|
|
+ out_ << '"';
|
|
|
+ out_.write(t.data(), t.size());
|
|
|
+ out_ << '"';
|
|
|
+ }
|
|
|
+
|
|
|
+ virtual void visit(const Integer& integer)
|
|
|
+ {
|
|
|
+ out_ << integer.i();
|
|
|
+ }
|
|
|
+
|
|
|
+ virtual void visit(const Bool& boolValue)
|
|
|
+ {
|
|
|
+ out_ << (boolValue.val() ? "true" : "false");
|
|
|
+ }
|
|
|
+
|
|
|
+ virtual void visit(const Null& nullValue)
|
|
|
+ {
|
|
|
+ out_ << "null";
|
|
|
+ }
|
|
|
+
|
|
|
+ virtual void visit(const List& list)
|
|
|
+ {
|
|
|
+ out_ << '[';
|
|
|
+ List::ValueType::const_iterator i = list.begin();
|
|
|
+ if(!list.empty()) {
|
|
|
+ (*i)->accept(*this);
|
|
|
+ }
|
|
|
+ ++i;
|
|
|
+ for(List::ValueType::const_iterator eoi = list.end(); i != eoi; ++i){
|
|
|
+ out_ << ',';
|
|
|
+ (*i)->accept(*this);
|
|
|
+ }
|
|
|
+ out_ << ']';
|
|
|
+ }
|
|
|
+
|
|
|
+ virtual void visit(const Dict& dict)
|
|
|
+ {
|
|
|
+ out_ << '{';
|
|
|
+ Dict::ValueType::const_iterator i = dict.begin();
|
|
|
+ if(!dict.empty()) {
|
|
|
+ std::string key = jsonEscape((*i).first);
|
|
|
+ out_ << '"';
|
|
|
+ out_.write(key.data(), key.size());
|
|
|
+ out_ << "\":";
|
|
|
+ (*i).second->accept(*this);
|
|
|
+ }
|
|
|
+ ++i;
|
|
|
+ for(Dict::ValueType::const_iterator eoi = dict.end(); i != eoi; ++i){
|
|
|
+ out_ << ',';
|
|
|
+ std::string key = jsonEscape((*i).first);
|
|
|
+ out_ << '"';
|
|
|
+ out_.write(key.data(), key.size());
|
|
|
+ out_ << "\":";
|
|
|
+ (*i).second->accept(*this);
|
|
|
+ }
|
|
|
+ out_ << '}';
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string getResult() const
|
|
|
+ {
|
|
|
+ return out_.str();
|
|
|
+ }
|
|
|
+ };
|
|
|
+ JsonValueBaseVisitor visitor;
|
|
|
+ vlb->accept(visitor);
|
|
|
+ return visitor.getResult();
|
|
|
+}
|
|
|
+
|
|
|
+// Serializes JSON object or array.
|
|
|
+std::string encode(const SharedHandle<ValueBase>& json)
|
|
|
+{
|
|
|
+ std::ostringstream out;
|
|
|
+ return encode(out, json.get()).str();
|
|
|
+}
|
|
|
+
|
|
|
+} // namespace json
|
|
|
+
|
|
|
+} // namespace aria2
|