json2proto.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. #include "json2proto.h"
  2. #include "util.h"
  3. #include <library/cpp/json/json_value.h>
  4. #include <library/cpp/protobuf/json/proto/enum_options.pb.h>
  5. #include <google/protobuf/util/time_util.h>
  6. #include <google/protobuf/message.h>
  7. #include <google/protobuf/descriptor.h>
  8. #include <util/generic/hash.h>
  9. #include <util/generic/maybe.h>
  10. #include <util/string/ascii.h>
  11. #include <util/string/cast.h>
  12. #define JSON_TO_FIELD(EProtoCppType, name, json, JsonCheckType, ProtoSet, JsonGet) \
  13. case FieldDescriptor::EProtoCppType: { \
  14. if (config.CastRobust) { \
  15. reflection->ProtoSet(&proto, &field, json.JsonGet##Robust()); \
  16. break; \
  17. } \
  18. if (!json.JsonCheckType()) { \
  19. if (config.CastFromString && json.IsString()) { \
  20. if (config.DoNotCastEmptyStrings && json.GetString().empty()) { \
  21. /* Empty string is same as "no value" for scalar types.*/ \
  22. break; \
  23. } \
  24. reflection->ProtoSet(&proto, &field, FromString(json.GetString())); \
  25. break; \
  26. } \
  27. ythrow yexception() << "Invalid type of JSON field " << name << ": " \
  28. << #JsonCheckType << "() failed while " \
  29. << #EProtoCppType << " is expected."; \
  30. } \
  31. reflection->ProtoSet(&proto, &field, json.JsonGet()); \
  32. break; \
  33. }
  34. static TString GetFieldName(const google::protobuf::FieldDescriptor& field,
  35. const NProtobufJson::TJson2ProtoConfig& config) {
  36. if (config.NameGenerator) {
  37. return config.NameGenerator(field);
  38. }
  39. if (config.UseJsonName) {
  40. Y_ASSERT(!field.json_name().empty());
  41. TString name = field.json_name();
  42. if (!field.has_json_name() && !name.empty()) {
  43. // FIXME: https://st.yandex-team.ru/CONTRIB-139
  44. name[0] = AsciiToLower(name[0]);
  45. }
  46. return name;
  47. }
  48. TString name = field.name();
  49. switch (config.FieldNameMode) {
  50. case NProtobufJson::TJson2ProtoConfig::FieldNameOriginalCase:
  51. break;
  52. case NProtobufJson::TJson2ProtoConfig::FieldNameLowerCase:
  53. name.to_lower();
  54. break;
  55. case NProtobufJson::TJson2ProtoConfig::FieldNameUpperCase:
  56. name.to_upper();
  57. break;
  58. case NProtobufJson::TJson2ProtoConfig::FieldNameCamelCase:
  59. if (!name.empty()) {
  60. name[0] = AsciiToLower(name[0]);
  61. }
  62. break;
  63. case NProtobufJson::TJson2ProtoConfig::FieldNameSnakeCase:
  64. NProtobufJson::ToSnakeCase(&name);
  65. break;
  66. case NProtobufJson::TJson2ProtoConfig::FieldNameSnakeCaseDense:
  67. NProtobufJson::ToSnakeCaseDense(&name);
  68. break;
  69. default:
  70. Y_DEBUG_ABORT_UNLESS(false, "Unknown FieldNameMode.");
  71. }
  72. return name;
  73. }
  74. static void JsonString2Duration(const NJson::TJsonValue& json,
  75. google::protobuf::Message& proto,
  76. const google::protobuf::FieldDescriptor& field,
  77. const NProtobufJson::TJson2ProtoConfig& config) {
  78. using namespace google::protobuf;
  79. if (json.IsMap()) {
  80. const auto& m = json.GetMap();
  81. Duration duration;
  82. duration.set_seconds(m.contains("Seconds") ? m.at("Seconds").GetInteger() : 0);
  83. duration.set_nanos(m.contains("Nanos") ? m.at("Nanos").GetInteger() : 0);
  84. proto.CopyFrom(duration);
  85. return;
  86. } else if (!json.GetString() && !config.CastRobust) {
  87. ythrow yexception() << "Invalid type of JSON field '" << field.name() << "': "
  88. << "IsString() failed while "
  89. << "CPPTYPE_STRING is expected.";
  90. }
  91. TString jsonString = json.GetStringRobust();
  92. Duration durationFromString;
  93. if (!util::TimeUtil::FromString(jsonString, &durationFromString)) {
  94. ythrow yexception() << "error while parsing google.protobuf.Duration from string on field '" <<
  95. field.name() << "'";
  96. }
  97. proto.CopyFrom(durationFromString);
  98. }
  99. static void JsonString2Timestamp(const NJson::TJsonValue& json,
  100. google::protobuf::Message& proto,
  101. const google::protobuf::FieldDescriptor& field,
  102. const NProtobufJson::TJson2ProtoConfig& config) {
  103. using namespace google::protobuf;
  104. if (!json.GetString() && !config.CastRobust) {
  105. ythrow yexception() << "Invalid type of JSON field '" << field.name() << "': "
  106. << "IsString() failed while "
  107. << "CPPTYPE_STRING is expected.";
  108. }
  109. TString jsonString = json.GetStringRobust();
  110. Timestamp timestampFromString;
  111. if (!util::TimeUtil::FromString(jsonString, &timestampFromString)) {
  112. ythrow yexception() << "error while parsing google.protobuf.Timestamp from string on field '" <<
  113. field.name() << "'";
  114. }
  115. proto.CopyFrom(timestampFromString);
  116. }
  117. static void
  118. JsonString2Field(const NJson::TJsonValue& json,
  119. google::protobuf::Message& proto,
  120. const google::protobuf::FieldDescriptor& field,
  121. const NProtobufJson::TJson2ProtoConfig& config) {
  122. using namespace google::protobuf;
  123. const Reflection* reflection = proto.GetReflection();
  124. Y_ASSERT(!!reflection);
  125. if (!json.IsString() && !config.CastRobust) {
  126. ythrow yexception() << "Invalid type of JSON field '" << field.name() << "': "
  127. << "IsString() failed while "
  128. << "CPPTYPE_STRING is expected.";
  129. }
  130. TString value = json.GetStringRobust();
  131. for (size_t i = 0, endI = config.StringTransforms.size(); i < endI; ++i) {
  132. Y_ASSERT(!!config.StringTransforms[i]);
  133. if (!!config.StringTransforms[i]) {
  134. if (field.type() == google::protobuf::FieldDescriptor::TYPE_BYTES) {
  135. config.StringTransforms[i]->TransformBytes(value);
  136. } else {
  137. config.StringTransforms[i]->Transform(value);
  138. }
  139. }
  140. }
  141. if (field.is_repeated())
  142. reflection->AddString(&proto, &field, std::move(value));
  143. else
  144. reflection->SetString(&proto, &field, std::move(value));
  145. }
  146. static bool
  147. HandleString2TimeConversion(const NJson::TJsonValue& json,
  148. google::protobuf::Message& proto,
  149. const google::protobuf::FieldDescriptor& field,
  150. const NProtobufJson::TJson2ProtoConfig& config) {
  151. using namespace google::protobuf;
  152. auto type = proto.GetDescriptor()->well_known_type();
  153. if (type == Descriptor::WellKnownType::WELLKNOWNTYPE_DURATION) {
  154. JsonString2Duration(json, proto, field, config);
  155. return true;
  156. } else if (type == Descriptor::WellKnownType::WELLKNOWNTYPE_TIMESTAMP) {
  157. JsonString2Timestamp(json, proto, field, config);
  158. return true;
  159. }
  160. return false;
  161. }
  162. static const NProtoBuf::EnumValueDescriptor*
  163. FindEnumValue(const NProtoBuf::EnumDescriptor* enumField,
  164. TStringBuf target, bool (*equals)(TStringBuf, TStringBuf)) {
  165. for (int i = 0; i < enumField->value_count(); i++) {
  166. auto* valueDescriptor = enumField->value(i);
  167. if (equals(valueDescriptor->name(), target)) {
  168. return valueDescriptor;
  169. }
  170. }
  171. return nullptr;
  172. }
  173. static const NProtoBuf::EnumValueDescriptor*
  174. FindEnumValueByJsonName(const NProtoBuf::EnumDescriptor* enumField, TStringBuf target) {
  175. for (int i = 0; i < enumField->value_count(); i++) {
  176. auto* valueDescriptor = enumField->value(i);
  177. if (valueDescriptor->options().GetExtension(json_enum_value) == target) {
  178. return valueDescriptor;
  179. }
  180. }
  181. return nullptr;
  182. }
  183. static void
  184. JsonEnum2Field(const NJson::TJsonValue& json,
  185. google::protobuf::Message& proto,
  186. const google::protobuf::FieldDescriptor& field,
  187. const NProtobufJson::TJson2ProtoConfig& config) {
  188. using namespace google::protobuf;
  189. const Reflection* reflection = proto.GetReflection();
  190. Y_ASSERT(!!reflection);
  191. const EnumDescriptor* enumField = field.enum_type();
  192. Y_ASSERT(!!enumField);
  193. /// @todo configure name/numerical value
  194. const EnumValueDescriptor* enumFieldValue = nullptr;
  195. if (json.IsInteger()) {
  196. const auto value = json.GetInteger();
  197. enumFieldValue = enumField->FindValueByNumber(value);
  198. if (!enumFieldValue) {
  199. ythrow yexception() << "Invalid integer value of JSON enum field: " << value << ".";
  200. }
  201. } else if (json.IsString()) {
  202. const auto& value = json.GetString();
  203. if (config.UseJsonEnumValue) {
  204. enumFieldValue = FindEnumValueByJsonName(enumField, value);
  205. } else {
  206. if (config.EnumValueMode == NProtobufJson::TJson2ProtoConfig::EnumCaseInsensetive) {
  207. enumFieldValue = FindEnumValue(enumField, value, AsciiEqualsIgnoreCase);
  208. } else if (config.EnumValueMode == NProtobufJson::TJson2ProtoConfig::EnumSnakeCaseInsensitive) {
  209. enumFieldValue = FindEnumValue(enumField, value, NProtobufJson::EqualsIgnoringCaseAndUnderscores);
  210. } else {
  211. enumFieldValue = enumField->FindValueByName(value);
  212. }
  213. }
  214. if (!enumFieldValue) {
  215. ythrow yexception() << "Invalid string value of JSON enum field: " << TStringBuf(value).Head(100) << ".";
  216. }
  217. } else {
  218. ythrow yexception() << "Invalid type of JSON enum field: not an integer/string.";
  219. }
  220. if (field.is_repeated()) {
  221. reflection->AddEnum(&proto, &field, enumFieldValue);
  222. } else {
  223. reflection->SetEnum(&proto, &field, enumFieldValue);
  224. }
  225. }
  226. static void
  227. Json2SingleField(const NJson::TJsonValue& json,
  228. google::protobuf::Message& proto,
  229. const google::protobuf::FieldDescriptor& field,
  230. const NProtobufJson::TJson2ProtoConfig& config,
  231. bool isMapValue = false) {
  232. using namespace google::protobuf;
  233. const Reflection* reflection = proto.GetReflection();
  234. Y_ASSERT(!!reflection);
  235. const NJson::TJsonValue* fieldJsonPtr = &json;
  236. TString nameHolder;
  237. TStringBuf name;
  238. if (!isMapValue) {
  239. nameHolder = GetFieldName(field, config);
  240. name = nameHolder;
  241. const NJson::TJsonValue& fieldJson = json[name];
  242. if (auto fieldJsonType = fieldJson.GetType(); fieldJsonType == NJson::JSON_UNDEFINED || fieldJsonType == NJson::JSON_NULL) {
  243. if (field.is_required() && !field.has_default_value() && !reflection->HasField(proto, &field) && config.CheckRequiredFields) {
  244. ythrow yexception() << "JSON has no field for required field "
  245. << name << ".";
  246. }
  247. return;
  248. }
  249. if (name) { // For compatibility with previous implementation. Not sure if GetFieldName is allowed to return empty strings,
  250. fieldJsonPtr = &fieldJson;
  251. }
  252. }
  253. const NJson::TJsonValue& fieldJson = *fieldJsonPtr;
  254. if (name && config.UnknownFieldsCollector) {
  255. config.UnknownFieldsCollector->OnEnterMapItem(nameHolder);
  256. }
  257. switch (field.cpp_type()) {
  258. JSON_TO_FIELD(CPPTYPE_INT32, field.name(), fieldJson, IsInteger, SetInt32, GetInteger);
  259. JSON_TO_FIELD(CPPTYPE_INT64, field.name(), fieldJson, IsInteger, SetInt64, GetInteger);
  260. JSON_TO_FIELD(CPPTYPE_UINT32, field.name(), fieldJson, IsInteger, SetUInt32, GetInteger);
  261. JSON_TO_FIELD(CPPTYPE_UINT64, field.name(), fieldJson, IsUInteger, SetUInt64, GetUInteger);
  262. JSON_TO_FIELD(CPPTYPE_DOUBLE, field.name(), fieldJson, IsDouble, SetDouble, GetDouble);
  263. JSON_TO_FIELD(CPPTYPE_FLOAT, field.name(), fieldJson, IsDouble, SetFloat, GetDouble);
  264. JSON_TO_FIELD(CPPTYPE_BOOL, field.name(), fieldJson, IsBoolean, SetBool, GetBoolean);
  265. case FieldDescriptor::CPPTYPE_STRING: {
  266. JsonString2Field(fieldJson, proto, field, config);
  267. break;
  268. }
  269. case FieldDescriptor::CPPTYPE_ENUM: {
  270. JsonEnum2Field(fieldJson, proto, field, config);
  271. break;
  272. }
  273. case FieldDescriptor::CPPTYPE_MESSAGE: {
  274. Message* innerProto = reflection->MutableMessage(&proto, &field);
  275. Y_ASSERT(!!innerProto);
  276. if (config.AllowString2TimeConversion &&
  277. HandleString2TimeConversion(fieldJson, *innerProto, field, config)) {
  278. break;
  279. }
  280. NProtobufJson::MergeJson2Proto(fieldJson, *innerProto, config);
  281. break;
  282. }
  283. default:
  284. ythrow yexception() << "Unknown protobuf field type: "
  285. << static_cast<int>(field.cpp_type()) << ".";
  286. }
  287. if (name && config.UnknownFieldsCollector) {
  288. config.UnknownFieldsCollector->OnLeaveMapItem();
  289. }
  290. }
  291. static void
  292. SetKey(NProtoBuf::Message& proto,
  293. const NProtoBuf::FieldDescriptor& field,
  294. const TString& key) {
  295. using namespace google::protobuf;
  296. using namespace NProtobufJson;
  297. const Reflection* reflection = proto.GetReflection();
  298. TString result;
  299. switch (field.cpp_type()) {
  300. case FieldDescriptor::CPPTYPE_INT32:
  301. reflection->SetInt32(&proto, &field, FromString<int32>(key));
  302. break;
  303. case FieldDescriptor::CPPTYPE_INT64:
  304. reflection->SetInt64(&proto, &field, FromString<int64>(key));
  305. break;
  306. case FieldDescriptor::CPPTYPE_UINT32:
  307. reflection->SetUInt32(&proto, &field, FromString<uint32>(key));
  308. break;
  309. case FieldDescriptor::CPPTYPE_UINT64:
  310. reflection->SetUInt64(&proto, &field, FromString<uint64>(key));
  311. break;
  312. case FieldDescriptor::CPPTYPE_BOOL:
  313. reflection->SetBool(&proto, &field, FromString<bool>(key));
  314. break;
  315. case FieldDescriptor::CPPTYPE_STRING:
  316. reflection->SetString(&proto, &field, key);
  317. break;
  318. default:
  319. ythrow yexception() << "Unsupported key type.";
  320. }
  321. }
  322. static void
  323. Json2RepeatedFieldValue(const NJson::TJsonValue& jsonValue,
  324. google::protobuf::Message& proto,
  325. const google::protobuf::FieldDescriptor& field,
  326. const NProtobufJson::TJson2ProtoConfig& config,
  327. const google::protobuf::Reflection* reflection,
  328. const TString* key = nullptr) {
  329. using namespace google::protobuf;
  330. switch (field.cpp_type()) {
  331. JSON_TO_FIELD(CPPTYPE_INT32, field.name(), jsonValue, IsInteger, AddInt32, GetInteger);
  332. JSON_TO_FIELD(CPPTYPE_INT64, field.name(), jsonValue, IsInteger, AddInt64, GetInteger);
  333. JSON_TO_FIELD(CPPTYPE_UINT32, field.name(), jsonValue, IsInteger, AddUInt32, GetInteger);
  334. JSON_TO_FIELD(CPPTYPE_UINT64, field.name(), jsonValue, IsUInteger, AddUInt64, GetUInteger);
  335. JSON_TO_FIELD(CPPTYPE_DOUBLE, field.name(), jsonValue, IsDouble, AddDouble, GetDouble);
  336. JSON_TO_FIELD(CPPTYPE_FLOAT, field.name(), jsonValue, IsDouble, AddFloat, GetDouble);
  337. JSON_TO_FIELD(CPPTYPE_BOOL, field.name(), jsonValue, IsBoolean, AddBool, GetBoolean);
  338. case FieldDescriptor::CPPTYPE_STRING: {
  339. JsonString2Field(jsonValue, proto, field, config);
  340. break;
  341. }
  342. case FieldDescriptor::CPPTYPE_ENUM: {
  343. JsonEnum2Field(jsonValue, proto, field, config);
  344. break;
  345. }
  346. case FieldDescriptor::CPPTYPE_MESSAGE: {
  347. Message* innerProto = reflection->AddMessage(&proto, &field);
  348. Y_ASSERT(!!innerProto);
  349. if (key) {
  350. const FieldDescriptor* keyField = innerProto->GetDescriptor()->map_key();
  351. Y_ENSURE(keyField, "Map entry key field not found: " << field.name());
  352. SetKey(*innerProto, *keyField, *key);
  353. const FieldDescriptor* valueField = innerProto->GetDescriptor()->map_value();
  354. Y_ENSURE(valueField, "Map entry value field not found.");
  355. Json2SingleField(jsonValue, *innerProto, *valueField, config, /*isMapValue=*/true);
  356. } else {
  357. NProtobufJson::MergeJson2Proto(jsonValue, *innerProto, config);
  358. }
  359. break;
  360. }
  361. default:
  362. ythrow yexception() << "Unknown protobuf field type: "
  363. << static_cast<int>(field.cpp_type()) << ".";
  364. }
  365. }
  366. static void
  367. Json2RepeatedField(const NJson::TJsonValue& json,
  368. google::protobuf::Message& proto,
  369. const google::protobuf::FieldDescriptor& field,
  370. const NProtobufJson::TJson2ProtoConfig& config) {
  371. using namespace google::protobuf;
  372. TString name = GetFieldName(field, config);
  373. const NJson::TJsonValue& fieldJson = json[name];
  374. if (fieldJson.GetType() == NJson::JSON_UNDEFINED || fieldJson.GetType() == NJson::JSON_NULL)
  375. return;
  376. if (config.UnknownFieldsCollector) {
  377. config.UnknownFieldsCollector->OnEnterMapItem(name);
  378. }
  379. bool isMap = fieldJson.GetType() == NJson::JSON_MAP;
  380. if (isMap) {
  381. if (!config.MapAsObject) {
  382. ythrow yexception() << "Map as object representation is not allowed, field: " << field.name();
  383. } else if (!field.is_map() && !fieldJson.GetMap().empty()) {
  384. ythrow yexception() << "Field " << field.name() << " is not a map.";
  385. }
  386. }
  387. if (fieldJson.GetType() != NJson::JSON_ARRAY && !config.MapAsObject && !config.VectorizeScalars && !config.ValueVectorizer) {
  388. ythrow yexception() << "JSON field doesn't represent an array for "
  389. << name
  390. << "(actual type is "
  391. << static_cast<int>(fieldJson.GetType()) << ").";
  392. }
  393. const Reflection* reflection = proto.GetReflection();
  394. Y_ASSERT(!!reflection);
  395. if (isMap) {
  396. const THashMap<TString, NJson::TJsonValue>& jsonMap = fieldJson.GetMap();
  397. for (const auto& x : jsonMap) {
  398. const TString& key = x.first;
  399. const NJson::TJsonValue& jsonValue = x.second;
  400. if (config.UnknownFieldsCollector) {
  401. config.UnknownFieldsCollector->OnEnterMapItem(key);
  402. }
  403. Json2RepeatedFieldValue(jsonValue, proto, field, config, reflection, &key);
  404. if (config.UnknownFieldsCollector) {
  405. config.UnknownFieldsCollector->OnLeaveMapItem();
  406. }
  407. }
  408. } else {
  409. if (config.ReplaceRepeatedFields) {
  410. reflection->ClearField(&proto, &field);
  411. }
  412. if (fieldJson.GetType() == NJson::JSON_ARRAY) {
  413. const NJson::TJsonValue::TArray& jsonArray = fieldJson.GetArray();
  414. ui64 id = 0;
  415. for (const NJson::TJsonValue& jsonValue : jsonArray) {
  416. if (config.UnknownFieldsCollector) {
  417. config.UnknownFieldsCollector->OnEnterArrayItem(id);
  418. }
  419. Json2RepeatedFieldValue(jsonValue, proto, field, config, reflection);
  420. if (config.UnknownFieldsCollector) {
  421. config.UnknownFieldsCollector->OnLeaveArrayItem();
  422. }
  423. ++id;
  424. }
  425. } else if (config.ValueVectorizer) {
  426. ui64 id = 0;
  427. for (const NJson::TJsonValue& jsonValue : config.ValueVectorizer(fieldJson)) {
  428. if (config.UnknownFieldsCollector) {
  429. config.UnknownFieldsCollector->OnEnterArrayItem(id);
  430. }
  431. Json2RepeatedFieldValue(jsonValue, proto, field, config, reflection);
  432. if (config.UnknownFieldsCollector) {
  433. config.UnknownFieldsCollector->OnLeaveArrayItem();
  434. }
  435. ++id;
  436. }
  437. } else if (config.VectorizeScalars) {
  438. Json2RepeatedFieldValue(fieldJson, proto, field, config, reflection);
  439. }
  440. }
  441. if (config.UnknownFieldsCollector) {
  442. config.UnknownFieldsCollector->OnLeaveMapItem();
  443. }
  444. }
  445. namespace NProtobufJson {
  446. void MergeJson2Proto(const NJson::TJsonValue& json, google::protobuf::Message& proto, const TJson2ProtoConfig& config) {
  447. if (json.IsNull()) {
  448. return;
  449. }
  450. const google::protobuf::Descriptor* descriptor = proto.GetDescriptor();
  451. Y_ASSERT(!!descriptor);
  452. Y_ENSURE(json.IsMap(), "Failed to merge json to proto for message: " << descriptor->full_name() << ", expected json map.");
  453. for (int f = 0, endF = descriptor->field_count(); f < endF; ++f) {
  454. const google::protobuf::FieldDescriptor* field = descriptor->field(f);
  455. Y_ASSERT(!!field);
  456. if (field->is_repeated()) {
  457. Json2RepeatedField(json, proto, *field, config);
  458. } else {
  459. Json2SingleField(json, proto, *field, config);
  460. }
  461. }
  462. if (!config.AllowUnknownFields || config.UnknownFieldsCollector) {
  463. THashMap<TString, bool> knownFields;
  464. for (int f = 0, endF = descriptor->field_count(); f < endF; ++f) {
  465. const google::protobuf::FieldDescriptor* field = descriptor->field(f);
  466. knownFields[GetFieldName(*field, config)] = 1;
  467. }
  468. for (const auto& f : json.GetMap()) {
  469. const bool isFieldKnown = knownFields.contains(f.first);
  470. Y_ENSURE(config.AllowUnknownFields || isFieldKnown, "unknown field \"" << f.first << "\" for \"" << descriptor->full_name() << "\"");
  471. if (!isFieldKnown) {
  472. config.UnknownFieldsCollector->OnUnknownField(f.first, *descriptor);
  473. }
  474. }
  475. }
  476. }
  477. void MergeJson2Proto(const TStringBuf& json, google::protobuf::Message& proto, const TJson2ProtoConfig& config) {
  478. NJson::TJsonReaderConfig jsonCfg;
  479. jsonCfg.DontValidateUtf8 = true;
  480. jsonCfg.AllowComments = config.AllowComments;
  481. NJson::TJsonValue jsonValue;
  482. ReadJsonTree(json, &jsonCfg, &jsonValue, /* throwOnError = */ true);
  483. MergeJson2Proto(jsonValue, proto, config);
  484. }
  485. void Json2Proto(const NJson::TJsonValue& json, google::protobuf::Message& proto, const TJson2ProtoConfig& config) {
  486. proto.Clear();
  487. MergeJson2Proto(json, proto, config);
  488. }
  489. void Json2Proto(const TStringBuf& json, google::protobuf::Message& proto, const TJson2ProtoConfig& config) {
  490. proto.Clear();
  491. MergeJson2Proto(json, proto, config);
  492. }
  493. }