json2proto.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. #include "json2proto.h"
  2. #include "util.h"
  3. #include <library/cpp/json/json_value.h>
  4. #include <google/protobuf/message.h>
  5. #include <google/protobuf/descriptor.h>
  6. #include <util/generic/hash.h>
  7. #include <util/generic/maybe.h>
  8. #include <util/string/ascii.h>
  9. #include <util/string/cast.h>
  10. #define JSON_TO_FIELD(EProtoCppType, name, json, JsonCheckType, ProtoSet, JsonGet) \
  11. case FieldDescriptor::EProtoCppType: { \
  12. if (config.CastRobust) { \
  13. reflection->ProtoSet(&proto, &field, json.JsonGet##Robust()); \
  14. break; \
  15. } \
  16. if (!json.JsonCheckType()) { \
  17. if (config.CastFromString && json.IsString()) { \
  18. if (config.DoNotCastEmptyStrings && json.GetString().empty()) { \
  19. /* Empty string is same as "no value" for scalar types.*/ \
  20. break; \
  21. } \
  22. reflection->ProtoSet(&proto, &field, FromString(json.GetString())); \
  23. break; \
  24. } \
  25. ythrow yexception() << "Invalid type of JSON field " << name << ": " \
  26. << #JsonCheckType << "() failed while " \
  27. << #EProtoCppType << " is expected."; \
  28. } \
  29. reflection->ProtoSet(&proto, &field, json.JsonGet()); \
  30. break; \
  31. }
  32. static TString GetFieldName(const google::protobuf::FieldDescriptor& field,
  33. const NProtobufJson::TJson2ProtoConfig& config) {
  34. if (config.NameGenerator) {
  35. return config.NameGenerator(field);
  36. }
  37. if (config.UseJsonName) {
  38. Y_ASSERT(!field.json_name().empty());
  39. TString name = field.json_name();
  40. if (!field.has_json_name() && !name.empty()) {
  41. // FIXME: https://st.yandex-team.ru/CONTRIB-139
  42. name[0] = AsciiToLower(name[0]);
  43. }
  44. return name;
  45. }
  46. TString name = field.name();
  47. switch (config.FieldNameMode) {
  48. case NProtobufJson::TJson2ProtoConfig::FieldNameOriginalCase:
  49. break;
  50. case NProtobufJson::TJson2ProtoConfig::FieldNameLowerCase:
  51. name.to_lower();
  52. break;
  53. case NProtobufJson::TJson2ProtoConfig::FieldNameUpperCase:
  54. name.to_upper();
  55. break;
  56. case NProtobufJson::TJson2ProtoConfig::FieldNameCamelCase:
  57. if (!name.empty()) {
  58. name[0] = AsciiToLower(name[0]);
  59. }
  60. break;
  61. case NProtobufJson::TJson2ProtoConfig::FieldNameSnakeCase:
  62. NProtobufJson::ToSnakeCase(&name);
  63. break;
  64. case NProtobufJson::TJson2ProtoConfig::FieldNameSnakeCaseDense:
  65. NProtobufJson::ToSnakeCaseDense(&name);
  66. break;
  67. default:
  68. Y_VERIFY_DEBUG(false, "Unknown FieldNameMode.");
  69. }
  70. return name;
  71. }
  72. static void
  73. JsonString2Field(const NJson::TJsonValue& json,
  74. google::protobuf::Message& proto,
  75. const google::protobuf::FieldDescriptor& field,
  76. const NProtobufJson::TJson2ProtoConfig& config) {
  77. using namespace google::protobuf;
  78. const Reflection* reflection = proto.GetReflection();
  79. Y_ASSERT(!!reflection);
  80. if (!json.IsString() && !config.CastRobust) {
  81. ythrow yexception() << "Invalid type of JSON field '" << field.name() << "': "
  82. << "IsString() failed while "
  83. << "CPPTYPE_STRING is expected.";
  84. }
  85. TString value = json.GetStringRobust();
  86. for (size_t i = 0, endI = config.StringTransforms.size(); i < endI; ++i) {
  87. Y_ASSERT(!!config.StringTransforms[i]);
  88. if (!!config.StringTransforms[i]) {
  89. if (field.type() == google::protobuf::FieldDescriptor::TYPE_BYTES) {
  90. config.StringTransforms[i]->TransformBytes(value);
  91. } else {
  92. config.StringTransforms[i]->Transform(value);
  93. }
  94. }
  95. }
  96. if (field.is_repeated())
  97. reflection->AddString(&proto, &field, value);
  98. else
  99. reflection->SetString(&proto, &field, value);
  100. }
  101. static const NProtoBuf::EnumValueDescriptor*
  102. FindEnumValue(const NProtoBuf::EnumDescriptor* enumField,
  103. TStringBuf target, bool (*equals)(TStringBuf, TStringBuf)) {
  104. for (int i = 0; i < enumField->value_count(); i++) {
  105. auto* valueDescriptor = enumField->value(i);
  106. if (equals(valueDescriptor->name(), target)) {
  107. return valueDescriptor;
  108. }
  109. }
  110. return nullptr;
  111. }
  112. static void
  113. JsonEnum2Field(const NJson::TJsonValue& json,
  114. google::protobuf::Message& proto,
  115. const google::protobuf::FieldDescriptor& field,
  116. const NProtobufJson::TJson2ProtoConfig& config) {
  117. using namespace google::protobuf;
  118. const Reflection* reflection = proto.GetReflection();
  119. Y_ASSERT(!!reflection);
  120. const EnumDescriptor* enumField = field.enum_type();
  121. Y_ASSERT(!!enumField);
  122. /// @todo configure name/numerical value
  123. const EnumValueDescriptor* enumFieldValue = nullptr;
  124. if (json.IsInteger()) {
  125. const auto value = json.GetInteger();
  126. enumFieldValue = enumField->FindValueByNumber(value);
  127. if (!enumFieldValue) {
  128. ythrow yexception() << "Invalid integer value of JSON enum field: " << value << ".";
  129. }
  130. } else if (json.IsString()) {
  131. const auto& value = json.GetString();
  132. if (config.EnumValueMode == NProtobufJson::TJson2ProtoConfig::EnumCaseInsensetive) {
  133. enumFieldValue = FindEnumValue(enumField, value, AsciiEqualsIgnoreCase);
  134. } else if (config.EnumValueMode == NProtobufJson::TJson2ProtoConfig::EnumSnakeCaseInsensitive) {
  135. enumFieldValue = FindEnumValue(enumField, value, NProtobufJson::EqualsIgnoringCaseAndUnderscores);
  136. } else {
  137. enumFieldValue = enumField->FindValueByName(value);
  138. }
  139. if (!enumFieldValue) {
  140. ythrow yexception() << "Invalid string value of JSON enum field: " << TStringBuf(value).Head(100) << ".";
  141. }
  142. } else {
  143. ythrow yexception() << "Invalid type of JSON enum field: not an integer/string.";
  144. }
  145. if (field.is_repeated()) {
  146. reflection->AddEnum(&proto, &field, enumFieldValue);
  147. } else {
  148. reflection->SetEnum(&proto, &field, enumFieldValue);
  149. }
  150. }
  151. static void
  152. Json2SingleField(const NJson::TJsonValue& json,
  153. google::protobuf::Message& proto,
  154. const google::protobuf::FieldDescriptor& field,
  155. const NProtobufJson::TJson2ProtoConfig& config,
  156. bool isMapValue = false) {
  157. using namespace google::protobuf;
  158. const Reflection* reflection = proto.GetReflection();
  159. Y_ASSERT(!!reflection);
  160. TString name;
  161. if (!isMapValue) {
  162. name = GetFieldName(field, config);
  163. if (!json.Has(name) || json[name].GetType() == NJson::JSON_UNDEFINED || json[name].GetType() == NJson::JSON_NULL) {
  164. if (field.is_required() && !field.has_default_value() && !reflection->HasField(proto, &field) && config.CheckRequiredFields) {
  165. ythrow yexception() << "JSON has no field for required field "
  166. << name << ".";
  167. }
  168. return;
  169. }
  170. }
  171. const NJson::TJsonValue& fieldJson = name ? json[name] : json;
  172. switch (field.cpp_type()) {
  173. JSON_TO_FIELD(CPPTYPE_INT32, field.name(), fieldJson, IsInteger, SetInt32, GetInteger);
  174. JSON_TO_FIELD(CPPTYPE_INT64, field.name(), fieldJson, IsInteger, SetInt64, GetInteger);
  175. JSON_TO_FIELD(CPPTYPE_UINT32, field.name(), fieldJson, IsInteger, SetUInt32, GetInteger);
  176. JSON_TO_FIELD(CPPTYPE_UINT64, field.name(), fieldJson, IsUInteger, SetUInt64, GetUInteger);
  177. JSON_TO_FIELD(CPPTYPE_DOUBLE, field.name(), fieldJson, IsDouble, SetDouble, GetDouble);
  178. JSON_TO_FIELD(CPPTYPE_FLOAT, field.name(), fieldJson, IsDouble, SetFloat, GetDouble);
  179. JSON_TO_FIELD(CPPTYPE_BOOL, field.name(), fieldJson, IsBoolean, SetBool, GetBoolean);
  180. case FieldDescriptor::CPPTYPE_STRING: {
  181. JsonString2Field(fieldJson, proto, field, config);
  182. break;
  183. }
  184. case FieldDescriptor::CPPTYPE_ENUM: {
  185. JsonEnum2Field(fieldJson, proto, field, config);
  186. break;
  187. }
  188. case FieldDescriptor::CPPTYPE_MESSAGE: {
  189. Message* innerProto = reflection->MutableMessage(&proto, &field);
  190. Y_ASSERT(!!innerProto);
  191. NProtobufJson::MergeJson2Proto(fieldJson, *innerProto, config);
  192. break;
  193. }
  194. default:
  195. ythrow yexception() << "Unknown protobuf field type: "
  196. << static_cast<int>(field.cpp_type()) << ".";
  197. }
  198. }
  199. static void
  200. SetKey(NProtoBuf::Message& proto,
  201. const NProtoBuf::FieldDescriptor& field,
  202. const TString& key) {
  203. using namespace google::protobuf;
  204. using namespace NProtobufJson;
  205. const Reflection* reflection = proto.GetReflection();
  206. TString result;
  207. switch (field.cpp_type()) {
  208. case FieldDescriptor::CPPTYPE_INT32:
  209. reflection->SetInt32(&proto, &field, FromString<int32>(key));
  210. break;
  211. case FieldDescriptor::CPPTYPE_INT64:
  212. reflection->SetInt64(&proto, &field, FromString<int64>(key));
  213. break;
  214. case FieldDescriptor::CPPTYPE_UINT32:
  215. reflection->SetUInt32(&proto, &field, FromString<uint32>(key));
  216. break;
  217. case FieldDescriptor::CPPTYPE_UINT64:
  218. reflection->SetUInt64(&proto, &field, FromString<uint64>(key));
  219. break;
  220. case FieldDescriptor::CPPTYPE_BOOL:
  221. reflection->SetBool(&proto, &field, FromString<bool>(key));
  222. break;
  223. case FieldDescriptor::CPPTYPE_STRING:
  224. reflection->SetString(&proto, &field, key);
  225. break;
  226. default:
  227. ythrow yexception() << "Unsupported key type.";
  228. }
  229. }
  230. static void
  231. Json2RepeatedFieldValue(const NJson::TJsonValue& jsonValue,
  232. google::protobuf::Message& proto,
  233. const google::protobuf::FieldDescriptor& field,
  234. const NProtobufJson::TJson2ProtoConfig& config,
  235. const google::protobuf::Reflection* reflection,
  236. const TMaybe<TString>& key = {}) {
  237. using namespace google::protobuf;
  238. switch (field.cpp_type()) {
  239. JSON_TO_FIELD(CPPTYPE_INT32, field.name(), jsonValue, IsInteger, AddInt32, GetInteger);
  240. JSON_TO_FIELD(CPPTYPE_INT64, field.name(), jsonValue, IsInteger, AddInt64, GetInteger);
  241. JSON_TO_FIELD(CPPTYPE_UINT32, field.name(), jsonValue, IsInteger, AddUInt32, GetInteger);
  242. JSON_TO_FIELD(CPPTYPE_UINT64, field.name(), jsonValue, IsUInteger, AddUInt64, GetUInteger);
  243. JSON_TO_FIELD(CPPTYPE_DOUBLE, field.name(), jsonValue, IsDouble, AddDouble, GetDouble);
  244. JSON_TO_FIELD(CPPTYPE_FLOAT, field.name(), jsonValue, IsDouble, AddFloat, GetDouble);
  245. JSON_TO_FIELD(CPPTYPE_BOOL, field.name(), jsonValue, IsBoolean, AddBool, GetBoolean);
  246. case FieldDescriptor::CPPTYPE_STRING: {
  247. JsonString2Field(jsonValue, proto, field, config);
  248. break;
  249. }
  250. case FieldDescriptor::CPPTYPE_ENUM: {
  251. JsonEnum2Field(jsonValue, proto, field, config);
  252. break;
  253. }
  254. case FieldDescriptor::CPPTYPE_MESSAGE: {
  255. Message* innerProto = reflection->AddMessage(&proto, &field);
  256. Y_ASSERT(!!innerProto);
  257. if (key.Defined()) {
  258. const FieldDescriptor* keyField = innerProto->GetDescriptor()->FindFieldByName("key");
  259. Y_ENSURE(keyField, "Map entry key field not found: " << field.name());
  260. SetKey(*innerProto, *keyField, *key);
  261. const FieldDescriptor* valueField = innerProto->GetDescriptor()->FindFieldByName("value");
  262. Y_ENSURE(valueField, "Map entry value field not found.");
  263. Json2SingleField(jsonValue, *innerProto, *valueField, config, /*isMapValue=*/true);
  264. } else {
  265. NProtobufJson::MergeJson2Proto(jsonValue, *innerProto, config);
  266. }
  267. break;
  268. }
  269. default:
  270. ythrow yexception() << "Unknown protobuf field type: "
  271. << static_cast<int>(field.cpp_type()) << ".";
  272. }
  273. }
  274. static void
  275. Json2RepeatedField(const NJson::TJsonValue& json,
  276. google::protobuf::Message& proto,
  277. const google::protobuf::FieldDescriptor& field,
  278. const NProtobufJson::TJson2ProtoConfig& config) {
  279. using namespace google::protobuf;
  280. TString name = GetFieldName(field, config);
  281. if (!json.Has(name))
  282. return;
  283. const NJson::TJsonValue& fieldJson = json[name];
  284. if (fieldJson.GetType() == NJson::JSON_UNDEFINED || fieldJson.GetType() == NJson::JSON_NULL)
  285. return;
  286. bool isMap = fieldJson.GetType() == NJson::JSON_MAP;
  287. if (isMap) {
  288. if (!config.MapAsObject) {
  289. ythrow yexception() << "Map as object representation is not allowed, field: " << field.name();
  290. } else if (!field.is_map() && !fieldJson.GetMap().empty()) {
  291. ythrow yexception() << "Field " << field.name() << " is not a map.";
  292. }
  293. }
  294. if (fieldJson.GetType() != NJson::JSON_ARRAY && !config.MapAsObject && !config.VectorizeScalars && !config.ValueVectorizer) {
  295. ythrow yexception() << "JSON field doesn't represent an array for "
  296. << name
  297. << "(actual type is "
  298. << static_cast<int>(fieldJson.GetType()) << ").";
  299. }
  300. const Reflection* reflection = proto.GetReflection();
  301. Y_ASSERT(!!reflection);
  302. if (isMap) {
  303. const THashMap<TString, NJson::TJsonValue> jsonMap = fieldJson.GetMap();
  304. for (const auto& x : jsonMap) {
  305. const TString& key = x.first;
  306. const NJson::TJsonValue& jsonValue = x.second;
  307. Json2RepeatedFieldValue(jsonValue, proto, field, config, reflection, key);
  308. }
  309. } else {
  310. if (config.ReplaceRepeatedFields) {
  311. reflection->ClearField(&proto, &field);
  312. }
  313. if (fieldJson.GetType() == NJson::JSON_ARRAY) {
  314. const NJson::TJsonValue::TArray& jsonArray = fieldJson.GetArray();
  315. for (const NJson::TJsonValue& jsonValue : jsonArray) {
  316. Json2RepeatedFieldValue(jsonValue, proto, field, config, reflection);
  317. }
  318. } else if (config.ValueVectorizer) {
  319. for (const NJson::TJsonValue& jsonValue : config.ValueVectorizer(fieldJson)) {
  320. Json2RepeatedFieldValue(jsonValue, proto, field, config, reflection);
  321. }
  322. } else if (config.VectorizeScalars) {
  323. Json2RepeatedFieldValue(fieldJson, proto, field, config, reflection);
  324. }
  325. }
  326. }
  327. namespace NProtobufJson {
  328. void MergeJson2Proto(const NJson::TJsonValue& json, google::protobuf::Message& proto, const TJson2ProtoConfig& config) {
  329. if (json.IsNull()) {
  330. return;
  331. }
  332. Y_ENSURE(json.IsMap(), "expected json map");
  333. const google::protobuf::Descriptor* descriptor = proto.GetDescriptor();
  334. Y_ASSERT(!!descriptor);
  335. for (int f = 0, endF = descriptor->field_count(); f < endF; ++f) {
  336. const google::protobuf::FieldDescriptor* field = descriptor->field(f);
  337. Y_ASSERT(!!field);
  338. if (field->is_repeated()) {
  339. Json2RepeatedField(json, proto, *field, config);
  340. } else {
  341. Json2SingleField(json, proto, *field, config);
  342. }
  343. }
  344. if (!config.AllowUnknownFields) {
  345. THashMap<TString, bool> knownFields;
  346. for (int f = 0, endF = descriptor->field_count(); f < endF; ++f) {
  347. const google::protobuf::FieldDescriptor* field = descriptor->field(f);
  348. knownFields[GetFieldName(*field, config)] = 1;
  349. }
  350. for (const auto& f : json.GetMap()) {
  351. Y_ENSURE(knownFields.contains(f.first), "unknown field " << f.first);
  352. }
  353. }
  354. }
  355. void MergeJson2Proto(const TStringBuf& json, google::protobuf::Message& proto, const TJson2ProtoConfig& config) {
  356. NJson::TJsonReaderConfig jsonCfg;
  357. jsonCfg.DontValidateUtf8 = true;
  358. jsonCfg.AllowComments = config.AllowComments;
  359. NJson::TJsonValue jsonValue;
  360. ReadJsonTree(json, &jsonCfg, &jsonValue, /* throwOnError = */ true);
  361. MergeJson2Proto(jsonValue, proto, config);
  362. }
  363. void Json2Proto(const NJson::TJsonValue& json, google::protobuf::Message& proto, const TJson2ProtoConfig& config) {
  364. proto.Clear();
  365. MergeJson2Proto(json, proto, config);
  366. }
  367. void Json2Proto(const TStringBuf& json, google::protobuf::Message& proto, const TJson2ProtoConfig& config) {
  368. proto.Clear();
  369. MergeJson2Proto(json, proto, config);
  370. }
  371. }