#include "json.h"
#include "proto.h"
#include "proto2json.h"
#include <library/cpp/protobuf/json/ut/test.pb.h>
#include <library/cpp/json/json_value.h>
#include <library/cpp/json/json_reader.h>
#include <library/cpp/json/json_writer.h>
#include <library/cpp/protobuf/interop/cast.h>
#include <library/cpp/protobuf/json/json2proto.h>
#include <library/cpp/testing/unittest/registar.h>
#include <util/generic/hash_set.h>
#include <util/generic/string.h>
#include <util/generic/ylimits.h>
#include <util/stream/str.h>
#include <util/string/cast.h>
#include <util/system/defaults.h>
#include <util/system/yassert.h>
using namespace NProtobufJson;
using namespace NProtobufJsonTest;
namespace google {
namespace protobuf {
namespace internal {
void MapTestForceDeterministic() {
google::protobuf::io::CodedOutputStream::SetDefaultSerializationDeterministic();
}
}
} // namespace protobuf
}
namespace {
class TInit {
public:
TInit() {
::google::protobuf::internal::MapTestForceDeterministic();
}
} Init;
template <typename T>
TString ConvertToString(T value) {
return ToString(value);
}
// default ToString<double>() implementation loses precision
TString ConvertToString(double value) {
return FloatToString(value);
}
TString JsonValueToString(const NJson::TJsonValue& json) {
NJsonWriter::TBuf buf(NJsonWriter::HEM_UNSAFE);
return buf.WriteJsonValue(&json).Str();
}
void TestComplexMapAsObject(std::function<void(TComplexMapType&)>&& init, const TString& json, const TJson2ProtoConfig& config = TJson2ProtoConfig().SetMapAsObject(true)) {
TComplexMapType modelProto;
init(modelProto);
TString modelStr(json);
TComplexMapType proto;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TComplexMapType>(modelStr, config));
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
}
Y_UNIT_TEST_SUITE(TJson2ProtoTest) {
Y_UNIT_TEST(TestFlatOptional){
{const NJson::TJsonValue& json = CreateFlatJson();
TFlatOptional proto;
Json2Proto(json, proto);
TFlatOptional modelProto;
FillFlatProto(&modelProto);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
// Try to skip each field
#define DEFINE_FIELD(name, value) \
{ \
THashSet<TString> skippedField; \
skippedField.insert(#name); \
const NJson::TJsonValue& json = CreateFlatJson(skippedField); \
TFlatOptional proto; \
Json2Proto(json, proto); \
TFlatOptional modelProto; \
FillFlatProto(&modelProto, skippedField); \
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); \
}
#include <library/cpp/protobuf/json/ut/fields.incl>
#undef DEFINE_FIELD
} // TestFlatOptional
Y_UNIT_TEST(TestFlatRequired){
{const NJson::TJsonValue& json = CreateFlatJson();
TFlatRequired proto;
Json2Proto(json, proto);
TFlatRequired modelProto;
FillFlatProto(&modelProto);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
// Try to skip each field
#define DEFINE_FIELD(name, value) \
{ \
THashSet<TString> skippedField; \
skippedField.insert(#name); \
const NJson::TJsonValue& json = CreateFlatJson(skippedField); \
TFlatRequired proto; \
UNIT_ASSERT_EXCEPTION(Json2Proto(json, proto), yexception); \
}
#include <library/cpp/protobuf/json/ut/fields.incl>
#undef DEFINE_FIELD
} // TestFlatRequired
Y_UNIT_TEST(TestNameGenerator) {
TJson2ProtoConfig cfg;
cfg.SetNameGenerator([](const NProtoBuf::FieldDescriptor&) { return "42"; });
TNameGeneratorType proto;
Json2Proto(TStringBuf(R"({"42":42})"), proto, cfg);
TNameGeneratorType expected;
expected.SetField(42);
UNIT_ASSERT_PROTOS_EQUAL(expected, proto);
}
Y_UNIT_TEST(TestFlatNoCheckRequired) {
{
const NJson::TJsonValue& json = CreateFlatJson();
TFlatRequired proto;
Json2Proto(json, proto);
TFlatRequired modelProto;
FillFlatProto(&modelProto);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
TJson2ProtoConfig cfg;
cfg.CheckRequiredFields = false;
// Try to skip each field
#define DEFINE_FIELD(name, value) \
{ \
THashSet<TString> skippedField; \
skippedField.insert(#name); \
const NJson::TJsonValue& json = CreateFlatJson(skippedField); \
TFlatRequired proto; \
UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto, cfg)); \
}
#include <library/cpp/protobuf/json/ut/fields.incl>
#undef DEFINE_FIELD
} // TestFlatNoCheckRequired
Y_UNIT_TEST(TestFlatRepeated){
{const NJson::TJsonValue& json = CreateRepeatedFlatJson();
TFlatRepeated proto;
Json2Proto(json, proto);
TFlatRepeated modelProto;
FillRepeatedProto(&modelProto);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
// Try to skip each field
#define DEFINE_REPEATED_FIELD(name, ...) \
{ \
THashSet<TString> skippedField; \
skippedField.insert(#name); \
const NJson::TJsonValue& json = CreateRepeatedFlatJson(skippedField); \
TFlatRepeated proto; \
Json2Proto(json, proto); \
TFlatRepeated modelProto; \
FillRepeatedProto(&modelProto, skippedField); \
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); \
}
#include <library/cpp/protobuf/json/ut/repeated_fields.incl>
#undef DEFINE_REPEATED_FIELD
} // TestFlatRepeated
Y_UNIT_TEST(TestCompositeOptional){
{const NJson::TJsonValue& json = CreateCompositeJson();
TCompositeOptional proto;
Json2Proto(json, proto);
TCompositeOptional modelProto;
FillCompositeProto(&modelProto);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
// Try to skip each field
#define DEFINE_FIELD(name, value) \
{ \
THashSet<TString> skippedField; \
skippedField.insert(#name); \
const NJson::TJsonValue& json = CreateCompositeJson(skippedField); \
TCompositeOptional proto; \
Json2Proto(json, proto); \
TCompositeOptional modelProto; \
FillCompositeProto(&modelProto, skippedField); \
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); \
}
#include <library/cpp/protobuf/json/ut/fields.incl>
#undef DEFINE_FIELD
} // TestCompositeOptional
Y_UNIT_TEST(TestCompositeOptionalStringBuf){
{NJson::TJsonValue json = CreateCompositeJson();
json["Part"]["Double"] = 42.5;
TCompositeOptional proto;
Json2Proto(JsonValueToString(json), proto);
TCompositeOptional modelProto;
FillCompositeProto(&modelProto);
modelProto.MutablePart()->SetDouble(42.5);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
// Try to skip each field
#define DEFINE_FIELD(name, value) \
{ \
THashSet<TString> skippedField; \
skippedField.insert(#name); \
NJson::TJsonValue json = CreateCompositeJson(skippedField); \
if (json["Part"].Has("Double")) { \
json["Part"]["Double"] = 42.5; \
} \
TCompositeOptional proto; \
Json2Proto(JsonValueToString(json), proto); \
TCompositeOptional modelProto; \
FillCompositeProto(&modelProto, skippedField); \
if (modelProto.GetPart().HasDouble()) { \
modelProto.MutablePart()->SetDouble(42.5); \
} \
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto); \
}
#include <library/cpp/protobuf/json/ut/fields.incl>
#undef DEFINE_FIELD
} // TestCompositeOptionalStringBuf
Y_UNIT_TEST(TestCompositeRequired) {
{
const NJson::TJsonValue& json = CreateCompositeJson();
TCompositeRequired proto;
Json2Proto(json, proto);
TCompositeRequired modelProto;
FillCompositeProto(&modelProto);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
{
NJson::TJsonValue json;
TCompositeRequired proto;
UNIT_ASSERT_EXCEPTION(Json2Proto(json, proto), yexception);
}
} // TestCompositeRequired
Y_UNIT_TEST(TestCompositeRepeated) {
{
NJson::TJsonValue json;
NJson::TJsonValue array;
array.AppendValue(CreateFlatJson());
json.InsertValue("Part", array);
TCompositeRepeated proto;
Json2Proto(json, proto);
TFlatOptional partModelProto;
FillFlatProto(&partModelProto);
TCompositeRepeated modelProto;
modelProto.AddPart()->CopyFrom(partModelProto);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
{
// Array of messages with each field skipped
TCompositeRepeated modelProto;
NJson::TJsonValue array;
#define DEFINE_REPEATED_FIELD(name, ...) \
{ \
THashSet<TString> skippedField; \
skippedField.insert(#name); \
TFlatOptional partModelProto; \
FillFlatProto(&partModelProto, skippedField); \
modelProto.AddPart()->CopyFrom(partModelProto); \
array.AppendValue(CreateFlatJson(skippedField)); \
}
#include <library/cpp/protobuf/json/ut/repeated_fields.incl>
#undef DEFINE_REPEATED_FIELD
NJson::TJsonValue json;
json.InsertValue("Part", array);
TCompositeRepeated proto;
Json2Proto(json, proto);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
} // TestCompositeRepeated
Y_UNIT_TEST(TestInvalidEnum) {
{
NJson::TJsonValue json;
json.InsertValue("Enum", "E_100");
TFlatOptional proto;
UNIT_ASSERT_EXCEPTION(Json2Proto(json, proto), yexception);
}
{
NJson::TJsonValue json;
json.InsertValue("Enum", 100);
TFlatOptional proto;
UNIT_ASSERT_EXCEPTION(Json2Proto(json, proto), yexception);
}
}
Y_UNIT_TEST(TestFieldNameMode) {
// Original case 1
{
TString modelStr(R"_({"String":"value"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetString() == "value");
}
// Original case 2
{
TString modelStr(R"_({"String":"value"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
config.FieldNameMode = TJson2ProtoConfig::FieldNameOriginalCase;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetString() == "value");
}
// Lowercase
{
TString modelStr(R"_({"string":"value"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
config.FieldNameMode = TJson2ProtoConfig::FieldNameLowerCase;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetString() == "value");
}
// Uppercase
{
TString modelStr(R"_({"STRING":"value"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
config.FieldNameMode = TJson2ProtoConfig::FieldNameUpperCase;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetString() == "value");
}
// Camelcase
{
TString modelStr(R"_({"string":"value"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
config.FieldNameMode = TJson2ProtoConfig::FieldNameCamelCase;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetString() == "value");
}
{
TString modelStr(R"_({"oneString":"value"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
config.FieldNameMode = TJson2ProtoConfig::FieldNameCamelCase;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetOneString() == "value");
}
{
TString modelStr(R"_({"oneTwoString":"value"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
config.FieldNameMode = TJson2ProtoConfig::FieldNameCamelCase;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetOneTwoString() == "value");
}
// snake_case
{
TString modelStr(R"_({"string":"value"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
config.FieldNameMode = TJson2ProtoConfig::FieldNameSnakeCase;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetString() == "value");
}
{
TString modelStr(R"_({"one_string":"value"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
config.FieldNameMode = TJson2ProtoConfig::FieldNameSnakeCase;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetOneString() == "value");
}
{
TString modelStr(R"_({"one_two_string":"value"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
config.FieldNameMode = TJson2ProtoConfig::FieldNameSnakeCase;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetOneTwoString() == "value");
}
// Original case, repeated
{
TString modelStr(R"_({"I32":[1,2]})_");
TFlatRepeated proto;
TJson2ProtoConfig config;
config.FieldNameMode = TJson2ProtoConfig::FieldNameOriginalCase;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatRepeated>(modelStr, config));
UNIT_ASSERT(proto.I32Size() == 2);
UNIT_ASSERT(proto.GetI32(0) == 1);
UNIT_ASSERT(proto.GetI32(1) == 2);
}
// Lower case, repeated
{
TString modelStr(R"_({"i32":[1,2]})_");
TFlatRepeated proto;
TJson2ProtoConfig config;
config.FieldNameMode = TJson2ProtoConfig::FieldNameLowerCase;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatRepeated>(modelStr, config));
UNIT_ASSERT(proto.I32Size() == 2);
UNIT_ASSERT(proto.GetI32(0) == 1);
UNIT_ASSERT(proto.GetI32(1) == 2);
}
// UseJsonName
{
// FIXME(CONTRIB-139): since protobuf 3.1, Def_upper json name is
// "DefUpper", but until kernel/ugc/schema and yweb/yasap/pdb are
// updated, library/cpp/protobuf/json preserves compatibility with
// protobuf 3.0 by lowercasing default names, making it "defUpper".
TString modelStr(R"_({"My-Upper":1,"my-lower":2,"defUpper":3,"defLower":4})_");
TWithJsonName proto;
TJson2ProtoConfig config;
config.SetUseJsonName(true);
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TWithJsonName>(modelStr, config));
UNIT_ASSERT_EQUAL(proto.Getmy_upper(), 1);
UNIT_ASSERT_EQUAL(proto.GetMy_lower(), 2);
UNIT_ASSERT_EQUAL(proto.GetDef_upper(), 3);
UNIT_ASSERT_EQUAL(proto.Getdef_lower(), 4);
}
// FieldNameMode with UseJsonName
{
TJson2ProtoConfig config;
config.SetFieldNameMode(TJson2ProtoConfig::FieldNameLowerCase);
UNIT_ASSERT_EXCEPTION_CONTAINS(
config.SetUseJsonName(true), yexception, "mutually exclusive");
}
{
TJson2ProtoConfig config;
config.SetUseJsonName(true);
UNIT_ASSERT_EXCEPTION_CONTAINS(
config.SetFieldNameMode(TJson2ProtoConfig::FieldNameLowerCase), yexception, "mutually exclusive");
}
} // TestFieldNameMode
class TStringTransform: public IStringTransform {
public:
int GetType() const override {
return 0;
}
void Transform(TString& str) const override {
str = "transformed_any";
}
};
class TBytesTransform: public IStringTransform {
public:
int GetType() const override {
return 0;
}
void Transform(TString&) const override {
}
void TransformBytes(TString& str) const override {
str = "transformed_bytes";
}
};
Y_UNIT_TEST(TestInvalidJson) {
NJson::TJsonValue val{"bad value"};
TFlatOptional proto;
UNIT_ASSERT_EXCEPTION(Json2Proto(val, proto), yexception);
}
Y_UNIT_TEST(TestInvalidRepeatedFieldWithMapAsObject) {
TCompositeRepeated proto;
TJson2ProtoConfig config;
config.MapAsObject = true;
UNIT_ASSERT_EXCEPTION(Json2Proto(TStringBuf(R"({"Part":{"Boo":{}}})"), proto, config), yexception);
}
Y_UNIT_TEST(TestStringTransforms) {
// Check that strings and bytes are transformed
{
TString modelStr(R"_({"String":"value_str", "Bytes": "value_bytes"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
config.AddStringTransform(new TStringTransform);
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetString() == "transformed_any");
UNIT_ASSERT(proto.GetBytes() == "transformed_any");
}
// Check that bytes are transformed, strings are left intact
{
TString modelStr(R"_({"String":"value_str", "Bytes": "value_bytes"})_");
TFlatOptional proto;
TJson2ProtoConfig config;
config.AddStringTransform(new TBytesTransform);
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatOptional>(modelStr, config));
UNIT_ASSERT(proto.GetString() == "value_str");
UNIT_ASSERT(proto.GetBytes() == "transformed_bytes");
}
// Check that repeated bytes are transformed, repeated strings are left intact
{
TString modelStr(R"_({"String":["value_str", "str2"], "Bytes": ["value_bytes", "bytes2"]})_");
TFlatRepeated proto;
TJson2ProtoConfig config;
config.AddStringTransform(new TBytesTransform);
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TFlatRepeated>(modelStr, config));
UNIT_ASSERT(proto.StringSize() == 2);
UNIT_ASSERT(proto.GetString(0) == "value_str");
UNIT_ASSERT(proto.GetString(1) == "str2");
UNIT_ASSERT(proto.BytesSize() == 2);
UNIT_ASSERT(proto.GetBytes(0) == "transformed_bytes");
UNIT_ASSERT(proto.GetBytes(1) == "transformed_bytes");
}
// Check that bytes are transformed, strings are left intact in composed messages
{
TString modelStr(R"_({"Part": {"String":"value_str", "Bytes": "value_bytes"}})_");
TCompositeOptional proto;
TJson2ProtoConfig config;
config.AddStringTransform(new TBytesTransform);
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TCompositeOptional>(modelStr, config));
UNIT_ASSERT(proto.GetPart().GetString() == "value_str");
UNIT_ASSERT(proto.GetPart().GetBytes() == "transformed_bytes");
}
} // TestStringTransforms
Y_UNIT_TEST(TestCastFromString) {
// single fields
{
NJson::TJsonValue json;
#define DEFINE_FIELD(name, value) \
json.InsertValue(#name, ConvertToString(value));
#include <library/cpp/protobuf/json/ut/fields.incl>
#undef DEFINE_FIELD
TFlatOptional proto;
UNIT_ASSERT_EXCEPTION_CONTAINS(Json2Proto(json, proto), yexception, "Invalid type");
TJson2ProtoConfig config;
config.SetCastFromString(true);
Json2Proto(json, proto, config);
TFlatOptional modelProto;
FillFlatProto(&modelProto);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
// repeated fields
{
NJson::TJsonValue json;
#define DEFINE_REPEATED_FIELD(name, type, ...) \
{ \
type values[] = {__VA_ARGS__}; \
NJson::TJsonValue array(NJson::JSON_ARRAY); \
for (size_t i = 0, end = Y_ARRAY_SIZE(values); i < end; ++i) { \
array.AppendValue(ConvertToString(values[i])); \
} \
json.InsertValue(#name, array); \
}
#include <library/cpp/protobuf/json/ut/repeated_fields.incl>
#undef DEFINE_REPEATED_FIELD
TFlatRepeated proto;
UNIT_ASSERT_EXCEPTION_CONTAINS(Json2Proto(json, proto), yexception, "Invalid type");
TJson2ProtoConfig config;
config.SetCastFromString(true);
Json2Proto(json, proto, config);
TFlatRepeated modelProto;
FillRepeatedProto(&modelProto);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
}
} // TestCastFromString
Y_UNIT_TEST(TestMap) {
TMapType modelProto;
auto& items = *modelProto.MutableItems();
items["key1"] = "value1";
items["key2"] = "value2";
items["key3"] = "value3";
TString modelStr(R"_({"Items":[{"key":"key3","value":"value3"},{"key":"key2","value":"value2"},{"key":"key1","value":"value1"}]})_");
TJson2ProtoConfig config;
TMapType proto;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TMapType>(modelStr, config));
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
} // TestMap
Y_UNIT_TEST(TestCastRobust) {
NJson::TJsonValue json;
json["I32"] = "5";
json["Bool"] = 1;
json["String"] = 6;
json["Double"] = 8;
TFlatOptional proto;
UNIT_ASSERT_EXCEPTION_CONTAINS(Json2Proto(json, proto), yexception, "Invalid type");
TJson2ProtoConfig config;
config.SetCastRobust(true);
Json2Proto(json, proto, config);
TFlatOptional expected;
expected.SetI32(5);
expected.SetBool(true);
expected.SetString("6");
expected.SetDouble(8);
UNIT_ASSERT_PROTOS_EQUAL(proto, expected);
}
Y_UNIT_TEST(TestVectorizeScalars) {
NJson::TJsonValue json;
#define DEFINE_FIELD(name, value) \
json.InsertValue(#name, value);
#include <library/cpp/protobuf/json/ut/fields.incl>
#undef DEFINE_FIELD
TFlatRepeated proto;
TJson2ProtoConfig config;
config.SetVectorizeScalars(true);
Json2Proto(json, proto, config);
#define DEFINE_FIELD(name, value) \
UNIT_ASSERT_VALUES_EQUAL(proto.Get ## name(0), value);
#include <library/cpp/protobuf/json/ut/fields.incl>
#undef DEFINE_FIELD
}
Y_UNIT_TEST(TestValueVectorizer) {
{
// No ValueVectorizer
NJson::TJsonValue json;
json["RepeatedString"] = "123";
TJson2ProtoConfig config;
TSingleRepeatedString expected;
UNIT_ASSERT_EXCEPTION(Json2Proto(json, expected, config), yexception);
}
{
// ValueVectorizer replace original value by array
NJson::TJsonValue json;
json["RepeatedString"] = "123";
TJson2ProtoConfig config;
TSingleRepeatedString expected;
expected.AddRepeatedString("4");
expected.AddRepeatedString("5");
expected.AddRepeatedString("6");
config.ValueVectorizer = [](const NJson::TJsonValue& val) -> NJson::TJsonValue::TArray {
Y_UNUSED(val);
return {NJson::TJsonValue("4"), NJson::TJsonValue("5"), NJson::TJsonValue("6")};
};
TSingleRepeatedString actual;
Json2Proto(json, actual, config);
UNIT_ASSERT_PROTOS_EQUAL(expected, actual);
}
{
// ValueVectorizer replace original value by array and cast
NJson::TJsonValue json;
json["RepeatedInt"] = 123;
TJson2ProtoConfig config;
TSingleRepeatedInt expected;
expected.AddRepeatedInt(4);
expected.AddRepeatedInt(5);
expected.AddRepeatedInt(6);
config.ValueVectorizer = [](const NJson::TJsonValue& val) -> NJson::TJsonValue::TArray {
Y_UNUSED(val);
return {NJson::TJsonValue("4"), NJson::TJsonValue(5), NJson::TJsonValue("6")};
};
config.CastFromString = true;
TSingleRepeatedInt actual;
Json2Proto(json, actual, config);
UNIT_ASSERT_PROTOS_EQUAL(expected, actual);
}
}
Y_UNIT_TEST(TestMapAsObject) {
TMapType modelProto;
auto& items = *modelProto.MutableItems();
items["key1"] = "value1";
items["key2"] = "value2";
items["key3"] = "value3";
TString modelStr(R"_({"Items":{"key1":"value1","key2":"value2","key3":"value3"}})_");
TJson2ProtoConfig config;
config.MapAsObject = true;
TMapType proto;
UNIT_ASSERT_NO_EXCEPTION(proto = Json2Proto<TMapType>(modelStr, config));
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
} // TestMapAsObject
Y_UNIT_TEST(TestComplexMapAsObject_I32) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableI32();
items[1] = 1;
items[-2] = -2;
items[3] = 3;
},
R"_({"I32":{"1":1,"-2":-2,"3":3}})_");
} // TestComplexMapAsObject_I32
Y_UNIT_TEST(TestComplexMapAsObject_I64) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableI64();
items[2147483649L] = 2147483649L;
items[-2147483650L] = -2147483650L;
items[2147483651L] = 2147483651L;
},
R"_({"I64":{"2147483649":2147483649,"-2147483650":-2147483650,"2147483651":2147483651}})_");
} // TestComplexMapAsObject_I64
Y_UNIT_TEST(TestComplexMapAsObject_UI32) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableUI32();
items[1073741825U] = 1073741825U;
items[1073741826U] = 1073741826U;
items[1073741827U] = 1073741827U;
},
R"_({"UI32":{"1073741825":1073741825,"1073741826":1073741826,"1073741827":1073741827}})_");
} // TestComplexMapAsObject_UI32
Y_UNIT_TEST(TestComplexMapAsObject_UI64) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableUI64();
items[9223372036854775809UL] = 9223372036854775809UL;
items[9223372036854775810UL] = 9223372036854775810UL;
items[9223372036854775811UL] = 9223372036854775811UL;
},
R"_({"UI64":{"9223372036854775809":9223372036854775809,"9223372036854775810":9223372036854775810,"9223372036854775811":9223372036854775811}})_");
} // TestComplexMapAsObject_UI64
Y_UNIT_TEST(TestComplexMapAsObject_SI32) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableSI32();
items[1] = 1;
items[-2] = -2;
items[3] = 3;
},
R"_({"SI32":{"1":1,"-2":-2,"3":3}})_");
} // TestComplexMapAsObject_SI32
Y_UNIT_TEST(TestComplexMapAsObject_SI64) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableSI64();
items[2147483649L] = 2147483649L;
items[-2147483650L] = -2147483650L;
items[2147483651L] = 2147483651L;
},
R"_({"SI64":{"2147483649":2147483649,"-2147483650":-2147483650,"2147483651":2147483651}})_");
} // TestComplexMapAsObject_SI64
Y_UNIT_TEST(TestComplexMapAsObject_FI32) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableFI32();
items[1073741825U] = 1073741825U;
items[1073741826U] = 1073741826U;
items[1073741827U] = 1073741827U;
},
R"_({"FI32":{"1073741825":1073741825,"1073741826":1073741826,"1073741827":1073741827}})_");
} // TestComplexMapAsObject_FI32
Y_UNIT_TEST(TestComplexMapAsObject_FI64) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableFI64();
items[9223372036854775809UL] = 9223372036854775809UL;
items[9223372036854775810UL] = 9223372036854775810UL;
items[9223372036854775811UL] = 9223372036854775811UL;
},
R"_({"FI64":{"9223372036854775809":9223372036854775809,"9223372036854775810":9223372036854775810,"9223372036854775811":9223372036854775811}})_");
} // TestComplexMapAsObject_FI64
Y_UNIT_TEST(TestComplexMapAsObject_SFI32) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableSFI32();
items[1] = 1;
items[-2] = -2;
items[3] = 3;
},
R"_({"SFI32":{"1":1,"-2":-2,"3":3}})_");
} // TestComplexMapAsObject_SFI32
Y_UNIT_TEST(TestComplexMapAsObject_SFI64) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableSFI64();
items[2147483649L] = 2147483649L;
items[-2147483650L] = -2147483650L;
items[2147483651L] = 2147483651L;
},
R"_({"SFI64":{"2147483649":2147483649,"-2147483650":-2147483650,"2147483651":2147483651}})_");
} // TestComplexMapAsObject_SFI64
Y_UNIT_TEST(TestComplexMapAsObject_Bool) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableBool();
items[true] = true;
items[false] = false;
},
R"_({"Bool":{"true":true,"false":false}})_");
} // TestComplexMapAsObject_Bool
Y_UNIT_TEST(TestComplexMapAsObject_String) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableString();
items["key1"] = "value1";
items["key2"] = "value2";
items["key3"] = "value3";
items[""] = "value4";
},
R"_({"String":{"key1":"value1","key2":"value2","key3":"value3","":"value4"}})_");
} // TestComplexMapAsObject_String
Y_UNIT_TEST(TestComplexMapAsObject_Enum) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableEnum();
items["key1"] = EEnum::E_1;
items["key2"] = EEnum::E_2;
items["key3"] = EEnum::E_3;
},
R"_({"Enum":{"key1":1,"key2":2,"key3":3}})_");
} // TestComplexMapAsObject_Enum
Y_UNIT_TEST(TestComplexMapAsObject_EnumString) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableEnum();
items["key1"] = EEnum::E_1;
items["key2"] = EEnum::E_2;
items["key3"] = EEnum::E_3;
},
R"_({"Enum":{"key1":"E_1","key2":"E_2","key3":"E_3"}})_");
} // TestComplexMapAsObject_EnumString
Y_UNIT_TEST(TestComplexMapAsObject_EnumStringCaseInsensetive) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableEnum();
items["key1"] = EEnum::E_1;
items["key2"] = EEnum::E_2;
items["key3"] = EEnum::E_3;
},
R"_({"Enum":{"key1":"e_1","key2":"E_2","key3":"e_3"}})_",
TJson2ProtoConfig()
.SetMapAsObject(true)
.SetEnumValueMode(NProtobufJson::TJson2ProtoConfig::EnumCaseInsensetive)
);
} // TestComplexMapAsObject_EnumStringCaseInsensetive
Y_UNIT_TEST(TestComplexMapAsObject_EnumStringSnakeCaseInsensitive) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableEnum();
items["key1"] = EEnum::E_1;
items["key2"] = EEnum::E_2;
items["key3"] = EEnum::E_3;
},
R"_({"Enum":{"key1":"e1","key2":"_E_2_","key3":"e_3"}})_",
TJson2ProtoConfig()
.SetMapAsObject(true)
.SetEnumValueMode(NProtobufJson::TJson2ProtoConfig::EnumSnakeCaseInsensitive)
);
} // TestComplexMapAsObject_EnumStringCaseInsensetive
Y_UNIT_TEST(TestComplexMapAsObject_Float) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableFloat();
items["key1"] = 0.1f;
items["key2"] = 0.2f;
items["key3"] = 0.3f;
},
R"_({"Float":{"key1":0.1,"key2":0.2,"key3":0.3}})_");
} // TestComplexMapAsObject_Float
Y_UNIT_TEST(TestComplexMapAsObject_Double) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
auto& items = *proto.MutableDouble();
items["key1"] = 0.1L;
items["key2"] = 0.2L;
items["key3"] = 0.3L;
},
R"_({"Double":{"key1":0.1,"key2":0.2,"key3":0.3}})_");
} // TestComplexMapAsObject_Double
Y_UNIT_TEST(TestComplexMapAsObject_Nested) {
TestComplexMapAsObject(
[](TComplexMapType& proto) {
TComplexMapType inner;
auto& innerItems = *inner.MutableString();
innerItems["key"] = "value";
auto& items = *proto.MutableNested();
items["key1"] = inner;
items["key2"] = inner;
items["key3"] = inner;
},
R"_({"Nested":{"key1":{"String":{"key":"value"}},"key2":{"String":{"key":"value"}},"key3":{"String":{"key":"value"}}}})_");
} // TestComplexMapAsObject_Nested
Y_UNIT_TEST(TestMapAsObjectConfigNotSet) {
TString modelStr(R"_({"Items":{"key":"value"}})_");
TJson2ProtoConfig config;
UNIT_ASSERT_EXCEPTION_CONTAINS(
Json2Proto<TMapType>(modelStr, config), yexception,
"Map as object representation is not allowed");
} // TestMapAsObjectNotSet
Y_UNIT_TEST(TestMergeFlatOptional) {
const NJson::TJsonValue& json = CreateFlatJson();
NJson::TJsonValue patch;
patch["I32"] = 5;
patch["Bool"] = false;
patch["String"] = "abacaba";
patch["Double"] = 0.123;
TFlatOptional proto;
UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto));
UNIT_ASSERT_NO_EXCEPTION(MergeJson2Proto(patch, proto));
TFlatRequired modelProto;
FillFlatProto(&modelProto);
modelProto.SetI32(5);
modelProto.SetBool(false);
modelProto.SetString("abacaba");
modelProto.SetDouble(0.123);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
} // TestMergeFlatOptional
Y_UNIT_TEST(TestMergeFlatRequired) {
const NJson::TJsonValue& json = CreateFlatJson();
NJson::TJsonValue patch;
patch["I32"] = 5;
patch["Bool"] = false;
patch["String"] = "abacaba";
patch["Double"] = 0.123;
TFlatRequired proto;
UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto));
UNIT_ASSERT_NO_EXCEPTION(MergeJson2Proto(patch, proto));
TFlatRequired modelProto;
FillFlatProto(&modelProto);
modelProto.SetI32(5);
modelProto.SetBool(false);
modelProto.SetString("abacaba");
modelProto.SetDouble(0.123);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
} // TestMergeFlatRequired
Y_UNIT_TEST(TestMergeComposite) {
const NJson::TJsonValue& json = CreateCompositeJson();
NJson::TJsonValue patch;
patch["Part"]["I32"] = 5;
patch["Part"]["Bool"] = false;
patch["Part"]["String"] = "abacaba";
patch["Part"]["Double"] = 0.123;
TCompositeOptional proto;
UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto));
UNIT_ASSERT_NO_EXCEPTION(MergeJson2Proto(patch, proto));
TCompositeOptional modelProto;
FillCompositeProto(&modelProto);
modelProto.MutablePart()->SetI32(5);
modelProto.MutablePart()->SetBool(false);
modelProto.MutablePart()->SetString("abacaba");
modelProto.MutablePart()->SetDouble(0.123);
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
} // TestMergeComposite
Y_UNIT_TEST(TestMergeRepeatedReplace) {
const NJson::TJsonValue& json = CreateRepeatedFlatJson();
NJson::TJsonValue patch;
patch["I32"].AppendValue(5);
patch["I32"].AppendValue(6);
patch["String"].AppendValue("abacaba");
TFlatRepeated proto;
TJson2ProtoConfig config;
config.ReplaceRepeatedFields = true;
UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto));
UNIT_ASSERT_NO_EXCEPTION(MergeJson2Proto(patch, proto, config));
TFlatRepeated modelProto;
FillRepeatedProto(&modelProto);
modelProto.ClearI32();
modelProto.AddI32(5);
modelProto.AddI32(6);
modelProto.ClearString();
modelProto.AddString("abacaba");
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
} // TestMergeRepeatedReplace
Y_UNIT_TEST(TestMergeRepeatedAppend) {
const NJson::TJsonValue& json = CreateRepeatedFlatJson();
NJson::TJsonValue patch;
patch["I32"].AppendValue(5);
patch["I32"].AppendValue(6);
patch["String"].AppendValue("abacaba");
TFlatRepeated proto;
UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto));
UNIT_ASSERT_NO_EXCEPTION(MergeJson2Proto(patch, proto));
TFlatRepeated modelProto;
FillRepeatedProto(&modelProto);
modelProto.AddI32(5);
modelProto.AddI32(6);
modelProto.AddString("abacaba");
UNIT_ASSERT_PROTOS_EQUAL(proto, modelProto);
} // TestMergeRepeatedAppend
Y_UNIT_TEST(TestEmptyStringForCastFromString) {
NJson::TJsonValue json;
json["I32"] = "";
json["Bool"] = "";
json["OneString"] = "";
TJson2ProtoConfig config;
config.SetCastFromString(true);
config.SetDoNotCastEmptyStrings(true);
TFlatOptional proto;
UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto, config));
UNIT_ASSERT(!proto.HasBool());
UNIT_ASSERT(!proto.HasI32());
UNIT_ASSERT(proto.HasOneString());
UNIT_ASSERT_EQUAL("", proto.GetOneString());
} // TestEmptyStringForCastFromString
Y_UNIT_TEST(TestAllowComments) {
constexpr TStringBuf json = R"(
{
"I32": 4, // comment1
/*
comment2
{}
qwer
*/
"I64": 3423
}
)";
TJson2ProtoConfig config;
TFlatOptional proto;
UNIT_ASSERT_EXCEPTION_CONTAINS(Json2Proto(json, proto, config), yexception, "Error: Missing a name for object member");
config.SetAllowComments(true);
UNIT_ASSERT_NO_EXCEPTION(Json2Proto(json, proto, config));
UNIT_ASSERT_VALUES_EQUAL(proto.GetI32(), 4);
UNIT_ASSERT_VALUES_EQUAL(proto.GetI64(), 3423);
} // TestAllowComments
Y_UNIT_TEST(TestSimplifiedDuration) {
NJson::TJsonValue json;
TSingleDuration simpleDuration;
json["Duration"] = "10.1s";
NProtobufJson::Json2Proto(json, simpleDuration, NProtobufJson::TJson2ProtoConfig().SetAllowString2TimeConversion(true));
UNIT_ASSERT_EQUAL(NProtoInterop::CastFromProto(simpleDuration.GetDuration()), TDuration::MilliSeconds(10100));
} // TestSimplifiedDuration
Y_UNIT_TEST(TestUnwrappedDuration) {
NJson::TJsonValue json;
TSingleDuration duration;
json["Duration"]["seconds"] = 2;
NProtobufJson::Json2Proto(json, duration, NProtobufJson::TJson2ProtoConfig());
UNIT_ASSERT_EQUAL(NProtoInterop::CastFromProto(duration.GetDuration()), TDuration::MilliSeconds(2000));
} // TestUnwrappedDuration
Y_UNIT_TEST(TestSimplifiedTimestamp) {
NJson::TJsonValue json;
TSingleTimestamp simpleTimestamp;
json["Timestamp"] = "2014-08-26T15:52:15Z";
NProtobufJson::Json2Proto(json, simpleTimestamp, NProtobufJson::TJson2ProtoConfig().SetAllowString2TimeConversion(true));
UNIT_ASSERT_EQUAL(NProtoInterop::CastFromProto(simpleTimestamp.GetTimestamp()), TInstant::ParseIso8601("2014-08-26T15:52:15Z"));
} // TestSimplifiedTimestamp
} // TJson2ProtoTest