Add td_api::JsonValue support.

GitOrigin-RevId: b79580a42f72c195c7c76d213f277702ee035907
This commit is contained in:
levlam 2018-12-10 04:01:02 +03:00
parent c4c9707f7a
commit e2b8b72541
11 changed files with 247 additions and 21 deletions

View File

@ -379,6 +379,7 @@ set(TDLIB_SOURCE
td/telegram/HashtagHints.cpp
td/telegram/InlineQueriesManager.cpp
td/telegram/InputMessageText.cpp
td/telegram/JsonValue.cpp
td/telegram/LanguagePackManager.cpp
td/telegram/Location.cpp
td/telegram/Logging.cpp
@ -502,6 +503,7 @@ set(TDLIB_SOURCE
td/telegram/HashtagHints.h
td/telegram/InlineQueriesManager.h
td/telegram/InputMessageText.h
td/telegram/JsonValue.h
td/telegram/LanguagePackManager.h
td/telegram/Location.h
td/telegram/logevent/LogEvent.h

View File

@ -1825,19 +1825,43 @@ notificationGroup id:int32 chat_id:int53 total_count:int32 notifications:vector<
//@class OptionValue @description Represents the value of an option
//@description Boolean option @value The value of the option
//@description Represents a boolean option @value The value of the option
optionValueBoolean value:Bool = OptionValue;
//@description An unknown option or an option which has a default value
//@description Represents an unknown option or an option which has a default value
optionValueEmpty = OptionValue;
//@description An integer option @value The value of the option
//@description Represents an integer option @value The value of the option
optionValueInteger value:int32 = OptionValue;
//@description A string option @value The value of the option
//@description Represents a string option @value The value of the option
optionValueString value:string = OptionValue;
//@description Represents one member of a JSON object @key Member's key @value Member's value
jsonObjectMember key:string value:JsonValue = JsonObjectMember;
//@class JsonValue @description Represents a JSON value
//@description Represents a null JSON value
jsonValueNull = JsonValue;
//@description Represents a boolean JSON value @value The value
jsonValueBoolean value:Bool = JsonValue;
//@description Represents a numeric JSON value @value The value
jsonValueNumber value:double = JsonValue;
//@description Represents a string JSON value @value The value
jsonValueString value:string = JsonValue;
//@description Represents a JSON array @values The list of array elements
jsonValueArray values:vector<JsonValue> = JsonValue;
//@description Represents a JSON object @members The list of object members
jsonValueObject members:vector<jsonObjectMember> = JsonValue;
//@class UserPrivacySettingRule @description Represents a single rule for managing privacy settings
//@description A rule to allow all users to do something
@ -2728,6 +2752,12 @@ cleanFileName file_name:string = Text;
//@language_pack_database_path Path to the language pack database in which strings are stored @localization_target Localization target to which the language pack belongs @language_pack_id Language pack identifier @key Language pack key of the string to be returned
getLanguagePackString language_pack_database_path:string localization_target:string language_pack_id:string key:string = LanguagePackStringValue;
//@description Converts a JSON-serialized string to corresponding JsonValue object. This is an offline method. Can be called before authorization. Can be called synchronously @json The JSON-serialized string
getJsonValue json:string = JsonValue;
//@description Converts a JsonValue object to corresponding JSON-serialized string. This is an offline method. Can be called before authorization. Can be called synchronously @json_value The JsonValue object
getJsonString json_value:JsonValue = Text;
//@description Sends an inline query to a bot and returns its results. Returns an error with code 502 if the bot fails to answer the query before the query timeout expires @bot_user_id The identifier of the target bot
//@chat_id Identifier of the chat, where the query was sent @user_location Location of the user, only if needed @query Text of the query @offset Offset of the first entry to return

Binary file not shown.

View File

@ -88,8 +88,8 @@ void gen_from_json_constructor(StringBuilder &sb, const T *constructor, bool is_
for (auto &arg : constructor->args) {
sb << " {\n";
sb << " TRY_RESULT(value, get_json_object_field(from, \"" << tl::simple::gen_cpp_name(arg.name)
<< "\", JsonValue::Type::Null, true));\n";
sb << " if (value.type() != JsonValue::Type::Null) {\n";
<< "\", td::JsonValue::Type::Null, true));\n";
sb << " if (value.type() != td::JsonValue::Type::Null) {\n";
if (arg.type->type == tl::simple::Type::Bytes) {
sb << " TRY_STATUS(from_json_bytes(to." << tl::simple::gen_cpp_field_name(arg.name) << ", value));\n";
} else {

103
td/telegram/JsonValue.cpp Normal file
View File

@ -0,0 +1,103 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/JsonValue.h"
#include "td/utils/JsonBuilder.h"
#include "td/utils/misc.h"
namespace td {
static td_api::object_ptr<td_api::JsonValue> get_json_value_object(const JsonValue &json_value);
static td_api::object_ptr<td_api::jsonObjectMember> get_json_value_member_object(
const std::pair<MutableSlice, JsonValue> &json_value_member) {
return td_api::make_object<td_api::jsonObjectMember>(json_value_member.first.str(),
get_json_value_object(json_value_member.second));
}
static td_api::object_ptr<td_api::JsonValue> get_json_value_object(const JsonValue &json_value) {
switch (json_value.type()) {
case JsonValue::Type::Null:
return td_api::make_object<td_api::jsonValueNull>();
case JsonValue::Type::Boolean:
return td_api::make_object<td_api::jsonValueBoolean>(json_value.get_boolean());
case JsonValue::Type::Number:
return td_api::make_object<td_api::jsonValueNumber>(to_double(json_value.get_number()));
case JsonValue::Type::String:
return td_api::make_object<td_api::jsonValueString>(json_value.get_string().str());
case JsonValue::Type::Array:
return td_api::make_object<td_api::jsonValueArray>(transform(json_value.get_array(), get_json_value_object));
case JsonValue::Type::Object:
return td_api::make_object<td_api::jsonValueObject>(
transform(json_value.get_object(), get_json_value_member_object));
default:
UNREACHABLE();
return nullptr;
}
}
Result<td_api::object_ptr<td_api::JsonValue>> get_json_value(MutableSlice json) {
TRY_RESULT(json_value, json_decode(json));
return get_json_value_object(json_value);
}
namespace {
class JsonableJsonValue : public Jsonable {
public:
explicit JsonableJsonValue(const td_api::JsonValue *json_value) : json_value_(json_value) {
}
void store(JsonValueScope *scope) const {
if (json_value_ == nullptr) {
*scope << JsonNull();
return;
}
switch (json_value_->get_id()) {
case td_api::jsonValueNull::ID:
*scope << JsonNull();
break;
case td_api::jsonValueBoolean::ID:
*scope << static_cast<const td_api::jsonValueBoolean *>(json_value_)->value_;
break;
case td_api::jsonValueNumber::ID:
*scope << static_cast<const td_api::jsonValueNumber *>(json_value_)->value_;
break;
case td_api::jsonValueString::ID:
*scope << static_cast<const td_api::jsonValueString *>(json_value_)->value_;
break;
case td_api::jsonValueArray::ID: {
auto array = scope->enter_array();
for (auto &value : static_cast<const td_api::jsonValueArray *>(json_value_)->values_) {
array << JsonableJsonValue(value.get());
}
break;
}
case td_api::jsonValueObject::ID: {
auto object = scope->enter_object();
for (auto &member : static_cast<const td_api::jsonValueObject *>(json_value_)->members_) {
if (member != nullptr) {
object << ctie(member->key_, JsonableJsonValue(member->value_.get()));
}
}
break;
}
default:
UNREACHABLE();
}
}
private:
const td_api::JsonValue *json_value_;
};
} // namespace
string get_json_string(const td_api::JsonValue *json_value) {
return json_encode<string>(JsonableJsonValue(json_value));
}
} // namespace td

21
td/telegram/JsonValue.h Normal file
View File

@ -0,0 +1,21 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/utils/common.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace td {
Result<td_api::object_ptr<td_api::JsonValue>> get_json_value(MutableSlice json);
string get_json_string(const td_api::JsonValue *json_value);
} // namespace td

View File

@ -38,6 +38,7 @@
#include "td/telegram/Global.h"
#include "td/telegram/HashtagHints.h"
#include "td/telegram/InlineQueriesManager.h"
#include "td/telegram/JsonValue.h"
#include "td/telegram/LanguagePackManager.h"
#include "td/telegram/Logging.h"
#include "td/telegram/MessageEntity.h"
@ -3119,6 +3120,8 @@ bool Td::is_synchronous_request(int32 id) {
case td_api::getFileExtension::ID:
case td_api::cleanFileName::ID:
case td_api::getLanguagePackString::ID:
case td_api::getJsonValue::ID:
case td_api::getJsonString::ID:
case td_api::setLogStream::ID:
case td_api::getLogStream::ID:
case td_api::setLogVerbosityLevel::ID:
@ -3322,6 +3325,8 @@ td_api::object_ptr<td_api::Object> Td::static_request(td_api::object_ptr<td_api:
case td_api::getFileMimeType::ID:
case td_api::getFileExtension::ID:
case td_api::cleanFileName::ID:
case td_api::getJsonValue::ID:
case td_api::getJsonString::ID:
return true;
default:
return false;
@ -6771,7 +6776,7 @@ void Td::on_request(uint64 id, const td_api::getTextEntities &request) {
UNREACHABLE();
}
void Td::on_request(uint64 id, td_api::parseTextEntities &request) {
void Td::on_request(uint64 id, const td_api::parseTextEntities &request) {
UNREACHABLE();
}
@ -6791,6 +6796,14 @@ void Td::on_request(uint64 id, const td_api::getLanguagePackString &request) {
UNREACHABLE();
}
void Td::on_request(uint64 id, const td_api::getJsonValue &request) {
UNREACHABLE();
}
void Td::on_request(uint64 id, const td_api::getJsonString &request) {
UNREACHABLE();
}
void Td::on_request(uint64 id, const td_api::setLogStream &request) {
UNREACHABLE();
}
@ -6873,6 +6886,22 @@ td_api::object_ptr<td_api::Object> Td::do_static_request(const td_api::getLangua
request.language_pack_database_path_, request.localization_target_, request.language_pack_id_, request.key_);
}
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::getJsonValue &request) {
if (!check_utf8(request.json_)) {
return make_error(400, "JSON has invalid encoding");
}
auto result = get_json_value(request.json_);
if (result.is_error()) {
return make_error(400, result.error().message());
} else {
return result.move_as_ok();
}
}
td_api::object_ptr<td_api::Object> Td::do_static_request(const td_api::getJsonString &request) {
return td_api::make_object<td_api::text>(get_json_string(request.json_value_.get()));
}
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::setLogStream &request) {
auto result = Logging::set_current_stream(std::move(request.log_stream_));
if (result.is_ok()) {

View File

@ -910,7 +910,7 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, const td_api::getTextEntities &request);
void on_request(uint64 id, td_api::parseTextEntities &request);
void on_request(uint64 id, const td_api::parseTextEntities &request);
void on_request(uint64 id, const td_api::getFileMimeType &request);
@ -920,6 +920,10 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, const td_api::getLanguagePackString &request);
void on_request(uint64 id, const td_api::getJsonValue &request);
void on_request(uint64 id, const td_api::getJsonString &request);
void on_request(uint64 id, const td_api::setLogStream &request);
void on_request(uint64 id, const td_api::getLogStream &request);
@ -958,6 +962,8 @@ class Td final : public NetQueryCallback {
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getFileExtension &request);
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::cleanFileName &request);
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getLanguagePackString &request);
static td_api::object_ptr<td_api::Object> do_static_request(td_api::getJsonValue &request);
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getJsonString &request);
static td_api::object_ptr<td_api::Object> do_static_request(td_api::setLogStream &request);
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getLogStream &request);
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::setLogVerbosityLevel &request);

View File

@ -2382,11 +2382,40 @@ class CliClient final : public Actor {
} else if (op == "ptehs") {
execute(make_tl_object<td_api::parseTextEntities>(args, make_tl_object<td_api::textParseModeHTML>()));
} else if (op == "gfmt") {
send_request(make_tl_object<td_api::getFileMimeType>(trim(args)));
execute(make_tl_object<td_api::getFileMimeType>(trim(args)));
} else if (op == "gfe") {
send_request(make_tl_object<td_api::getFileExtension>(trim(args)));
execute(make_tl_object<td_api::getFileExtension>(trim(args)));
} else if (op == "cfn") {
send_request(make_tl_object<td_api::cleanFileName>(args));
execute(make_tl_object<td_api::cleanFileName>(args));
} else if (op == "gjv") {
execute(td_api::make_object<td_api::getJsonValue>(args));
} else if (op == "gjs") {
execute(td_api::make_object<td_api::getJsonString>());
execute(td_api::make_object<td_api::getJsonString>(td_api::make_object<td_api::jsonValueNull>()));
execute(td_api::make_object<td_api::getJsonString>(td_api::make_object<td_api::jsonValueBoolean>(true)));
execute(td_api::make_object<td_api::getJsonString>(td_api::make_object<td_api::jsonValueNumber>(123456789123.0)));
execute(
td_api::make_object<td_api::getJsonString>(td_api::make_object<td_api::jsonValueString>(string("aba\x00"
"caba",
8))));
auto inner_array = td_api::make_object<td_api::jsonValueArray>();
inner_array->values_.push_back(td_api::make_object<td_api::jsonValueBoolean>(false));
auto array = td_api::make_object<td_api::jsonValueArray>();
array->values_.push_back(nullptr);
array->values_.push_back(std::move(inner_array));
array->values_.push_back(td_api::make_object<td_api::jsonValueNull>());
array->values_.push_back(td_api::make_object<td_api::jsonValueNumber>(-1));
execute(td_api::make_object<td_api::getJsonString>(std::move(array)));
auto object = td_api::make_object<td_api::jsonValueObject>();
object->members_.push_back(
td_api::make_object<td_api::jsonObjectMember>("", td_api::make_object<td_api::jsonValueString>("test")));
object->members_.push_back(td_api::make_object<td_api::jsonObjectMember>("a", nullptr));
object->members_.push_back(nullptr);
object->members_.push_back(
td_api::make_object<td_api::jsonObjectMember>("a", td_api::make_object<td_api::jsonValueNull>()));
execute(td_api::make_object<td_api::getJsonString>(std::move(object)));
} else {
op_not_found_count++;
}

View File

@ -344,17 +344,17 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
if (parser.skip_start_with("false")) {
return JsonValue::create_boolean(false);
}
return Status::Error("Starts with 'f' -- false expected");
return Status::Error("Token starts with 'f' -- false expected");
case 't':
if (parser.skip_start_with("true")) {
return JsonValue::create_boolean(true);
}
return Status::Error("Starts with 't' -- true expected");
return Status::Error("Token starts with 't' -- true expected");
case 'n':
if (parser.skip_start_with("null")) {
return JsonValue();
}
return Status::Error("Starts with 'n' -- null expected");
return Status::Error("Token starts with 'n' -- null expected");
case '"': {
TRY_RESULT(slice, json_string_decode(parser));
return JsonValue::create_string(slice);
@ -368,7 +368,7 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
}
while (true) {
if (parser.empty()) {
return Status::Error("Unexpected end");
return Status::Error("Unexpected string end");
}
TRY_RESULT(value, do_json_decode(parser, max_depth - 1));
res.emplace_back(std::move(value));
@ -381,7 +381,10 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
parser.skip_whitespaces();
continue;
}
return Status::Error("Unexpected symbol");
if (parser.empty()) {
return Status::Error("Unexpected string end");
}
return Status::Error("Unexpected symbol while parsing JSON Array");
}
return JsonValue::create_array(std::move(res));
}
@ -394,7 +397,7 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
}
while (true) {
if (parser.empty()) {
return Status::Error("Unexpected end");
return Status::Error("Unexpected string end");
}
TRY_RESULT(key, json_string_decode(parser));
parser.skip_whitespaces();
@ -412,7 +415,10 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
parser.skip_whitespaces();
continue;
}
return Status::Error("Unexpected symbol");
if (parser.empty()) {
return Status::Error("Unexpected string end");
}
return Status::Error("Unexpected symbol while parsing JSON Object");
}
return JsonValue::make_object(std::move(res));
}
@ -434,7 +440,7 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
return JsonValue::create_number(num);
}
case 0:
return Status::Error("Unexpected end");
return Status::Error("Unexpected string end");
default: {
char next = parser.peek_char();
if (0 < next && next < 127) {

View File

@ -723,8 +723,8 @@ Status json_string_skip(Parser &parser) TD_WARN_UNUSED_RESULT;
Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) TD_WARN_UNUSED_RESULT;
Status do_json_skip(Parser &parser, int32 max_depth) TD_WARN_UNUSED_RESULT;
inline Result<JsonValue> json_decode(MutableSlice from) {
Parser parser(from);
inline Result<JsonValue> json_decode(MutableSlice json) {
Parser parser(json);
const int32 DEFAULT_MAX_DEPTH = 100;
auto result = do_json_decode(parser, DEFAULT_MAX_DEPTH);
if (result.is_ok()) {