TrinityCore
ProtobufJSON.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "ProtobufJSON.h"
19#include "Errors.h"
20#include "Log.h"
21#include "StringFormat.h"
22#include <google/protobuf/message.h>
23#include <rapidjson/writer.h>
24#include <rapidjson/reader.h>
25#include <rapidjson/stringbuffer.h>
26#include <stack>
27
29{
30public:
32
33 void WriteInt32(int32 value) { _writer.Int(value); }
34 void WriteInt64(int64 value) { _writer.Int64(value); }
35 void WriteUInt32(uint32 value) { _writer.Uint(value); }
36 void WriteUInt64(uint64 value) { _writer.Uint64(value); }
37 void WriteDouble(double value) { _writer.Double(value); }
38 void WriteFloat(float value) { _writer.Double(value); }
39 void WriteBool(bool value) { _writer.Bool(value); }
40 void WriteEnum(google::protobuf::EnumValueDescriptor const* value) { _writer.String(value->name()); }
41 void WriteString(std::string const& value) { _writer.String(value); }
42 void WriteMessage(google::protobuf::Message const& value);
43
44 std::string GetString() const { return std::string(_buffer.GetString(), _buffer.GetSize()); }
45
46private:
47 void WriteMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field);
48 void WriteSimpleMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field);
49 void WriteRepeatedMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field);
50
51 rapidjson::StringBuffer _buffer;
52 rapidjson::Writer<rapidjson::StringBuffer> _writer;
53};
54
55void Serializer::WriteMessage(google::protobuf::Message const& value)
56{
57 google::protobuf::Reflection const* reflection = value.GetReflection();
58 std::vector<google::protobuf::FieldDescriptor const*> fields;
59 reflection->ListFields(value, &fields);
60
61 _writer.StartObject();
62 for (std::size_t i = 0; i < fields.size(); ++i)
63 WriteMessageField(value, fields[i]);
64
65 _writer.EndObject();
66}
67
68void Serializer::WriteMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field)
69{
70 _writer.Key(field->name().c_str());
71 if (field->is_repeated())
72 {
73 _writer.StartArray();
74 WriteRepeatedMessageField(value, field);
75 _writer.EndArray();
76 }
77 else
78 WriteSimpleMessageField(value, field);
79}
80
81void Serializer::WriteSimpleMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field)
82{
83 google::protobuf::Reflection const* reflection = value.GetReflection();
84 switch (field->cpp_type())
85 {
86 case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
87 WriteInt32(reflection->GetInt32(value, field));
88 break;
89 case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
90 WriteInt64(reflection->GetInt64(value, field));
91 break;
92 case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
93 WriteUInt32(reflection->GetUInt32(value, field));
94 break;
95 case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
96 WriteUInt64(reflection->GetUInt64(value, field));
97 break;
98 case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
99 WriteDouble(reflection->GetDouble(value, field));
100 break;
101 case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
102 WriteFloat(reflection->GetFloat(value, field));
103 break;
104 case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
105 WriteBool(reflection->GetBool(value, field));
106 break;
107 case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
108 WriteEnum(reflection->GetEnum(value, field));
109 break;
110 case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
111 {
112 std::string strValue = reflection->GetString(value, field);
113 if (field->type() == google::protobuf::FieldDescriptor::TYPE_STRING)
114 WriteString(strValue);
115 else
116 {
117 _writer.StartArray();
118 for (std::size_t i = 0; i < strValue.length(); ++i)
119 WriteUInt32(uint32(strValue[i]));
120 _writer.EndArray();
121 }
122 break;
123 }
124 case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
125 WriteMessage(reflection->GetMessage(value, field));
126 break;
127 default:
128 break;
129 }
130}
131
132void Serializer::WriteRepeatedMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field)
133{
134 google::protobuf::Reflection const* reflection = value.GetReflection();
135 for (int32 i = 0; i < reflection->FieldSize(value, field); ++i)
136 {
137 switch (field->cpp_type())
138 {
139 case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
140 WriteInt32(reflection->GetRepeatedInt32(value, field, i));
141 break;
142 case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
143 WriteInt64(reflection->GetRepeatedInt64(value, field, i));
144 break;
145 case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
146 WriteUInt32(reflection->GetRepeatedUInt32(value, field, i));
147 break;
148 case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
149 WriteUInt64(reflection->GetRepeatedUInt64(value, field, i));
150 break;
151 case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
152 WriteDouble(reflection->GetRepeatedDouble(value, field, i));
153 break;
154 case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
155 WriteFloat(reflection->GetRepeatedFloat(value, field, i));
156 break;
157 case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
158 WriteBool(reflection->GetRepeatedBool(value, field, i));
159 break;
160 case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
161 WriteEnum(reflection->GetRepeatedEnum(value, field, i));
162 break;
163 case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
164 {
165 std::string strValue = reflection->GetRepeatedString(value, field, i);
166 if (field->type() == google::protobuf::FieldDescriptor::TYPE_STRING)
167 WriteString(strValue);
168 else
169 {
170 _writer.StartArray();
171 for (std::size_t j = 0; j < strValue.length(); ++j)
172 WriteUInt32(uint32(strValue[j]));
173 _writer.EndArray();
174 }
175 break;
176 }
177 case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
178 WriteMessage(reflection->GetRepeatedMessage(value, field, i));
179 break;
180 default:
181 break;
182 }
183 }
184}
185
186class Deserializer : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, Deserializer>
187{
188public:
189 bool ReadMessage(std::string const& json, google::protobuf::Message* message);
190
191 bool Key(const Ch* str, rapidjson::SizeType length, bool copy);
192 bool Null();
193 bool Bool(bool b);
194 bool Int(int32 i);
195 bool Uint(uint32 i);
196 bool Int64(int64 i);
197 bool Uint64(uint64 i);
198 bool Double(double d);
199 bool String(const Ch* str, rapidjson::SizeType length, bool copy);
200 bool StartObject();
201 bool EndObject(rapidjson::SizeType memberCount);
202 bool StartArray();
203 bool EndArray(rapidjson::SizeType memberCount);
204
205 std::vector<std::string> const& GetErrors() const { return _errors; }
206
207private:
208 bool CheckType(google::protobuf::FieldDescriptor::CppType expectedType);
209
210 rapidjson::Reader _reader;
211 std::stack<google::protobuf::FieldDescriptor const*> _state;
212 std::stack<google::protobuf::Message*> _objectState;
213 std::vector<std::string> _errors;
214};
215
216bool Deserializer::ReadMessage(std::string const& json, google::protobuf::Message* message)
217{
218 rapidjson::StringStream ss(json.c_str());
219
220 _objectState.push(message);
221
222 rapidjson::ParseResult result = _reader.Parse(ss, *this);
223
224 ASSERT(result.IsError() || (_objectState.empty() && _state.empty()));
225
226 return !result.IsError() && _errors.empty();
227}
228
229bool Deserializer::Key(const Ch* str, rapidjson::SizeType /*length*/, bool /*copy*/)
230{
231 google::protobuf::FieldDescriptor const* field = _objectState.top()->GetDescriptor()->FindFieldByName(str);
232 if (!field)
233 {
234 _errors.push_back(Trinity::StringFormat("Message {} has no field {}.", _objectState.top()->GetTypeName(), str));
235 return false;
236 }
237
238 _state.push(field);
239 return true;
240}
241
243{
244 _state.pop();
245 return true;
246}
247
248#define SET_FIELD(message, field, Type, val) do { \
249 if (!field->is_repeated()) \
250 message->GetReflection()->Set ## Type(message, field, val); \
251 else \
252 message->GetReflection()->Add ## Type(message, field, val); \
253 _state.pop(); \
254 } while (0)
255
257{
258 if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_BOOL))
259 return false;
260
261 SET_FIELD(_objectState.top(), _state.top(), Bool, b);
262 return true;
263}
264
266{
267 if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_INT32))
268 return false;
269
270 SET_FIELD(_objectState.top(), _state.top(), Int32, i);
271 return true;
272}
273
275{
276 google::protobuf::FieldDescriptor const* field = _state.top();
277 google::protobuf::Message* message = _objectState.top();
278 switch (field->cpp_type())
279 {
280 case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
281 SET_FIELD(message, field, UInt32, i);
282 break;
283 case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
284 {
285 if (field->type() != google::protobuf::FieldDescriptor::TYPE_BYTES)
286 {
287 _errors.emplace_back("Expected field type to be bytes but got string instead.");
288 return false;
289 }
290 std::string currentValue = message->GetReflection()->GetString(*message, field);
291 currentValue.append(1, (char)i);
292 message->GetReflection()->SetString(message, field, currentValue);
293 break;
294 }
295 default:
296 _errors.push_back(Trinity::StringFormat("Expected field type to be uint32 or string but got {} instead.", _state.top()->cpp_type_name()));
297 return false;
298 }
299
300 return true;
301}
302
304{
305 if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_INT64))
306 return false;
307
308 SET_FIELD(_objectState.top(), _state.top(), Int64, i);
309 return true;
310}
311
313{
314 if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_UINT64))
315 return false;
316
317 SET_FIELD(_objectState.top(), _state.top(), UInt64, i);
318 return true;
319}
320
322{
323 google::protobuf::FieldDescriptor const* field = _state.top();
324 google::protobuf::Message* message = _objectState.top();
325 switch (field->cpp_type())
326 {
327 case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
328 SET_FIELD(message, field, Float, float(d));
329 break;
330 case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
331 SET_FIELD(message, field, Double, d);
332 break;
333 default:
334 _errors.push_back(Trinity::StringFormat("Expected field type to be float or double but got {} instead.", _state.top()->cpp_type_name()));
335 return false;
336 }
337
338 return true;
339}
340
341bool Deserializer::String(const Ch* str, rapidjson::SizeType /*length*/, bool /*copy*/)
342{
343 google::protobuf::FieldDescriptor const* field = _state.top();
344 google::protobuf::Message* message = _objectState.top();
345 switch (field->cpp_type())
346 {
347 case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
348 {
349 google::protobuf::EnumValueDescriptor const* enumValue = field->enum_type()->FindValueByName(str);
350 if (!enumValue)
351 {
352 _errors.push_back(Trinity::StringFormat("Field {} enum {} does not have a value named {}.", field->full_name(), field->enum_type()->full_name(), str));
353 return false;
354 }
355
356 SET_FIELD(message, field, Enum, enumValue);
357 break;
358 }
359 case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
360 SET_FIELD(message, field, String, str);
361 break;
362 default:
363 _errors.push_back(Trinity::StringFormat("Expected field type to be string or enum but got {} instead.", _state.top()->cpp_type_name()));
364 return false;
365 }
366
367 return true;
368}
369
371{
372 // not a root object
373 if (!_state.empty())
374 {
375 if (_state.top()->cpp_type() != google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE)
376 {
377 _errors.push_back(Trinity::StringFormat("Expected field {} to be a message but got {} instead.", _state.top()->name(), _state.top()->cpp_type_name()));
378 return false;
379 }
380
381 google::protobuf::Message* containingMessage = _objectState.top();
382 if (!_state.top()->is_repeated())
383 _objectState.push(containingMessage->GetReflection()->MutableMessage(containingMessage, _state.top()));
384 else
385 _objectState.push(containingMessage->GetReflection()->AddMessage(containingMessage, _state.top()));
386 }
387 else if (_objectState.size() != 1)
388 return false;
389
390 return true;
391}
392
393bool Deserializer::EndObject(rapidjson::SizeType /*memberCount*/)
394{
395 if (!_state.empty() && !_state.top()->is_repeated())
396 _state.pop();
397
398 _objectState.pop();
399 return true;
400}
401
403{
404 if (_state.empty())
405 {
406 _errors.emplace_back("Root cannot be an array.");
407 return false;
408 }
409
410 if (_state.top()->is_repeated() ^ (_state.top()->type() != google::protobuf::FieldDescriptor::TYPE_BYTES))
411 {
412 _errors.push_back(Trinity::StringFormat("Expected field {} type to be exactly an array OR bytes but it was both or none.", _state.top()->full_name()));
413 return false;
414 }
415
416 return true;
417}
418
419bool Deserializer::CheckType(google::protobuf::FieldDescriptor::CppType expectedType)
420{
421 if (_state.top()->cpp_type() != expectedType)
422 {
423 _errors.push_back(Trinity::StringFormat("Expected field {} type to be {} but got {} instead.",
424 _state.top()->full_name(), google::protobuf::FieldDescriptor::CppTypeName(expectedType), _state.top()->cpp_type_name()));
425 return false;
426 }
427
428 return true;
429}
430
431bool Deserializer::EndArray(rapidjson::SizeType /*memberCount*/)
432{
433 _state.pop();
434 return true;
435}
436
437#undef SET_FIELD
438
439std::string JSON::Serialize(google::protobuf::Message const& message)
440{
441 Serializer serializer;
442 serializer.WriteMessage(message);
443 return serializer.GetString();
444}
445
446bool JSON::Deserialize(std::string const& json, google::protobuf::Message* message)
447{
448 Deserializer deserializer;
449 if (!deserializer.ReadMessage(json, message))
450 {
451 for (std::size_t i = 0; i < deserializer.GetErrors().size(); ++i)
452 TC_LOG_ERROR("json", "{}", deserializer.GetErrors()[i]);
453 return false;
454 }
455
456 return true;
457}
int64_t int64
Definition: Define.h:137
int32_t int32
Definition: Define.h:138
uint64_t uint64
Definition: Define.h:141
uint32_t uint32
Definition: Define.h:142
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define SET_FIELD(message, field, Type, val)
bool Uint64(uint64 i)
bool StartObject()
std::stack< google::protobuf::FieldDescriptor const * > _state
bool Uint(uint32 i)
bool Double(double d)
bool CheckType(google::protobuf::FieldDescriptor::CppType expectedType)
bool EndArray(rapidjson::SizeType memberCount)
std::vector< std::string > const & GetErrors() const
bool ReadMessage(std::string const &json, google::protobuf::Message *message)
bool Int64(int64 i)
std::vector< std::string > _errors
bool String(const Ch *str, rapidjson::SizeType length, bool copy)
bool Int(int32 i)
rapidjson::Reader _reader
bool Bool(bool b)
bool EndObject(rapidjson::SizeType memberCount)
std::stack< google::protobuf::Message * > _objectState
bool Key(const Ch *str, rapidjson::SizeType length, bool copy)
void WriteMessage(google::protobuf::Message const &value)
void WriteUInt32(uint32 value)
std::string GetString() const
void WriteSimpleMessageField(google::protobuf::Message const &value, google::protobuf::FieldDescriptor const *field)
void WriteBool(bool value)
void WriteString(std::string const &value)
void WriteMessageField(google::protobuf::Message const &value, google::protobuf::FieldDescriptor const *field)
void WriteEnum(google::protobuf::EnumValueDescriptor const *value)
void WriteFloat(float value)
rapidjson::Writer< rapidjson::StringBuffer > _writer
void WriteInt64(int64 value)
void WriteDouble(double value)
void WriteRepeatedMessageField(google::protobuf::Message const &value, google::protobuf::FieldDescriptor const *field)
rapidjson::StringBuffer _buffer
void WriteInt32(int32 value)
void WriteUInt64(uint64 value)
TC_SHARED_API bool Deserialize(std::string const &json, google::protobuf::Message *message)
TC_SHARED_API std::string Serialize(google::protobuf::Message const &message)
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
constexpr std::size_t size()
Definition: UpdateField.h:796