text: //
  // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
  //
  // 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/telegram/misc.h"
  
  #include "td/utils/algorithm.h"
  #include "td/utils/JsonBuilder.h"
  #include "td/utils/logging.h"
  #include "td/utils/misc.h"
  #include "td/utils/utf8.h"
  
  #include <utility>
  
  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);
  }
  
  Result<telegram_api::object_ptr<telegram_api::JSONValue>> get_input_json_value(MutableSlice json) {
    TRY_RESULT(json_value, get_json_value(json));
    return convert_json_value(std::move(json_value));
  }
  
  static td_api::object_ptr<td_api::jsonObjectMember> convert_json_value_member_object(
      const telegram_api::object_ptr<telegram_api::jsonObjectValue> &json_object_value) {
    CHECK(json_object_value != nullptr);
    return td_api::make_object<td_api::jsonObjectMember>(json_object_value->key_,
                                                         convert_json_value_object(json_object_value->value_));
  }
  
  td_api::object_ptr<td_api::JsonValue> convert_json_value_object(
      const tl_object_ptr<telegram_api::JSONValue> &json_value) {
    CHECK(json_value != nullptr);
    switch (json_value->get_id()) {
      case telegram_api::jsonNull::ID:
        return td_api::make_object<td_api::jsonValueNull>();
      case telegram_api::jsonBool::ID:
        return td_api::make_object<td_api::jsonValueBoolean>(
            static_cast<const telegram_api::jsonBool *>(json_value.get())->value_);
      case telegram_api::jsonNumber::ID:
        return td_api::make_object<td_api::jsonValueNumber>(
            static_cast<const telegram_api::jsonNumber *>(json_value.get())->value_);
      case telegram_api::jsonString::ID:
        return td_api::make_object<td_api::jsonValueString>(
            static_cast<const telegram_api::jsonString *>(json_value.get())->value_);
      case telegram_api::jsonArray::ID:
        return td_api::make_object<td_api::jsonValueArray>(
            transform(static_cast<const telegram_api::jsonArray *>(json_value.get())->value_, convert_json_value_object));
      case telegram_api::jsonObject::ID:
        return td_api::make_object<td_api::jsonValueObject>(transform(
            static_cast<const telegram_api::jsonObject *>(json_value.get())->value_, convert_json_value_member_object));
      default:
        UNREACHABLE();
        return nullptr;
    }
  }
  
  static telegram_api::object_ptr<telegram_api::jsonObjectValue> convert_json_value_member(
      td_api::object_ptr<td_api::jsonObjectMember> &&json_object_member) {
    CHECK(json_object_member != nullptr);
    if (!clean_input_string(json_object_member->key_)) {
      json_object_member->key_.clear();
    }
    return telegram_api::make_object<telegram_api::jsonObjectValue>(
        json_object_member->key_, convert_json_value(std::move(json_object_member->value_)));
  }
  
  tl_object_ptr<telegram_api::JSONValue> convert_json_value(td_api::object_ptr<td_api::JsonValue> &&json_value) {
    if (json_value == nullptr) {
      return td_api::make_object<telegram_api::jsonNull>();
    }
    switch (json_value->get_id()) {
      case td_api::jsonValueNull::ID:
        return telegram_api::make_object<telegram_api::jsonNull>();
      case td_api::jsonValueBoolean::ID:
        return telegram_api::make_object<telegram_api::jsonBool>(
            static_cast<const td_api::jsonValueBoolean *>(json_value.get())->value_);
      case td_api::jsonValueNumber::ID:
        return telegram_api::make_object<telegram_api::jsonNumber>(
            static_cast<const td_api::jsonValueNumber *>(json_value.get())->value_);
      case td_api::jsonValueString::ID: {
        auto &str = static_cast<td_api::jsonValueString *>(json_value.get())->value_;
        if (!clean_input_string(str)) {
          str.clear();
        }
        return telegram_api::make_object<telegram_api::jsonString>(str);
      }
      case td_api::jsonValueArray::ID:
        return telegram_api::make_object<telegram_api::jsonArray>(
            transform(std::move(static_cast<td_api::jsonValueArray *>(json_value.get())->values_), convert_json_value));
      case td_api::jsonValueObject::ID:
        return telegram_api::make_object<telegram_api::jsonObject>(transform(
            std::move(static_cast<td_api::jsonValueObject *>(json_value.get())->members_), convert_json_value_member));
      default:
        UNREACHABLE();
        return nullptr;
    }
  }
  
  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 << JsonBool(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: {
          auto &str = static_cast<const td_api::jsonValueString *>(json_value_)->value_;
          if (!check_utf8(str)) {
            LOG(ERROR) << "Have incorrect UTF-8 string " << str;
            *scope << "";
          } else {
            *scope << str;
          }
          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) {
              if (!check_utf8(member->key_)) {
                LOG(ERROR) << "Have incorrect UTF-8 object key " << member->key_;
              } else {
                object(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
  