json2proto.cpp 23 KB

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