TrinityCore
Loading...
Searching...
No Matches
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/reader.h>
24#include <rapidjson/writer.h>
25#include <stack>
26
28{
29public:
31
32 void WriteInt32(int32 value) { _writer.Int(value); }
33 void WriteInt64(int64 value) { _writer.Int64(value); }
34 void WriteUInt32(uint32 value) { _writer.Uint(value); }
35 void WriteUInt64(uint64 value) { _writer.Uint64(value); }
36 void WriteDouble(double value) { _writer.Double(value); }
37 void WriteFloat(float value) { _writer.Double(value); }
38 void WriteBool(bool value) { _writer.Bool(value); }
39 void WriteEnum(google::protobuf::EnumValueDescriptor const* value) { _writer.String(value->name()); }
40 void WriteString(std::string const& value) { _writer.String(value); }
41 void WriteMessage(google::protobuf::Message const& value);
42
43 std::string GetString() const { return std::string(_buffer.GetString(), _buffer.GetSize()); }
44
45private:
46 void WriteMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field);
47 void WriteSimpleMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field);
48 void WriteRepeatedMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field);
49
50 rapidjson::StringBuffer _buffer;
51 rapidjson::Writer<rapidjson::StringBuffer> _writer;
52};
53
54void Serializer::WriteMessage(google::protobuf::Message const& value)
55{
56 google::protobuf::Reflection const* reflection = value.GetReflection();
57 std::vector<google::protobuf::FieldDescriptor const*> fields;
58 reflection->ListFields(value, &fields);
59
60 _writer.StartObject();
61 for (std::size_t i = 0; i < fields.size(); ++i)
62 WriteMessageField(value, fields[i]);
63
64 _writer.EndObject();
65}
66
67void Serializer::WriteMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field)
68{
69 _writer.Key(field->name().c_str());
70 if (field->is_repeated())
71 {
72 _writer.StartArray();
73 WriteRepeatedMessageField(value, field);
74 _writer.EndArray();
75 }
76 else
77 WriteSimpleMessageField(value, field);
78}
79
80void Serializer::WriteSimpleMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field)
81{
82 google::protobuf::Reflection const* reflection = value.GetReflection();
83 switch (field->cpp_type())
84 {
85 case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
86 WriteInt32(reflection->GetInt32(value, field));
87 break;
88 case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
89 WriteInt64(reflection->GetInt64(value, field));
90 break;
91 case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
92 WriteUInt32(reflection->GetUInt32(value, field));
93 break;
94 case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
95 WriteUInt64(reflection->GetUInt64(value, field));
96 break;
97 case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
98 WriteDouble(reflection->GetDouble(value, field));
99 break;
100 case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
101 WriteFloat(reflection->GetFloat(value, field));
102 break;
103 case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
104 WriteBool(reflection->GetBool(value, field));
105 break;
106 case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
107 WriteEnum(reflection->GetEnum(value, field));
108 break;
109 case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
110 {
111 std::string strValue = reflection->GetString(value, field);
112 if (field->type() == google::protobuf::FieldDescriptor::TYPE_STRING)
113 WriteString(strValue);
114 else
115 {
116 _writer.StartArray();
117 for (std::size_t i = 0; i < strValue.length(); ++i)
118 WriteUInt32(uint32(strValue[i]));
119 _writer.EndArray();
120 }
121 break;
122 }
123 case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
124 WriteMessage(reflection->GetMessage(value, field));
125 break;
126 default:
127 break;
128 }
129}
130
131void Serializer::WriteRepeatedMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field)
132{
133 google::protobuf::Reflection const* reflection = value.GetReflection();
134 for (int32 i = 0; i < reflection->FieldSize(value, field); ++i)
135 {
136 switch (field->cpp_type())
137 {
138 case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
139 WriteInt32(reflection->GetRepeatedInt32(value, field, i));
140 break;
141 case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
142 WriteInt64(reflection->GetRepeatedInt64(value, field, i));
143 break;
144 case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
145 WriteUInt32(reflection->GetRepeatedUInt32(value, field, i));
146 break;
147 case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
148 WriteUInt64(reflection->GetRepeatedUInt64(value, field, i));
149 break;
150 case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
151 WriteDouble(reflection->GetRepeatedDouble(value, field, i));
152 break;
153 case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
154 WriteFloat(reflection->GetRepeatedFloat(value, field, i));
155 break;
156 case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
157 WriteBool(reflection->GetRepeatedBool(value, field, i));
158 break;
159 case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
160 WriteEnum(reflection->GetRepeatedEnum(value, field, i));
161 break;
162 case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
163 {
164 std::string strValue = reflection->GetRepeatedString(value, field, i);
165 if (field->type() == google::protobuf::FieldDescriptor::TYPE_STRING)
166 WriteString(strValue);
167 else
168 {
169 _writer.StartArray();
170 for (std::size_t j = 0; j < strValue.length(); ++j)
171 WriteUInt32(uint32(strValue[j]));
172 _writer.EndArray();
173 }
174 break;
175 }
176 case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
177 WriteMessage(reflection->GetRepeatedMessage(value, field, i));
178 break;
179 default:
180 break;
181 }
182 }
183}
184
185class Deserializer : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, Deserializer>
186{
187public:
188 bool ReadMessage(std::string const& json, google::protobuf::Message* message);
189
190 bool Key(Ch const* str, rapidjson::SizeType length, bool copy);
191 bool Null();
192 bool Bool(bool b);
193 bool Int(int32 i);
194 bool Uint(uint32 i);
195 bool Int64(int64 i);
196 bool Uint64(uint64 i);
197 bool Double(double d);
198 bool String(Ch const* str, rapidjson::SizeType length, bool copy);
199 bool StartObject();
200 bool EndObject(rapidjson::SizeType memberCount);
201 bool StartArray();
202 bool EndArray(rapidjson::SizeType memberCount);
203
204 std::vector<std::string> const& GetErrors() const { return _errors; }
205
206private:
207 bool CheckType(google::protobuf::FieldDescriptor::CppType expectedType);
208
209 rapidjson::Reader _reader;
210 std::stack<google::protobuf::FieldDescriptor const*> _state;
211 std::stack<google::protobuf::Message*> _objectState;
212 std::vector<std::string> _errors;
213};
214
215bool Deserializer::ReadMessage(std::string const& json, google::protobuf::Message* message)
216{
217 rapidjson::MemoryStream ms(json.data(), json.length());
218 rapidjson::EncodedInputStream<rapidjson::UTF8<>, rapidjson::MemoryStream> is(ms);
219
220 _objectState.push(message);
221
222 rapidjson::ParseResult result = _reader.Parse(is, *this);
223
224 ASSERT(result.IsError() || (_objectState.empty() && _state.empty()));
225
226 return !result.IsError() && _errors.empty();
227}
228
229bool Deserializer::Key(Ch const* 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(Ch const* 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:149
int32_t int32
Definition Define.h:150
uint64_t uint64
Definition Define.h:153
uint32_t uint32
Definition Define.h:154
#define ASSERT
Definition Errors.h:80
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define SET_FIELD(message, field, Type, val)
bool Uint64(uint64 i)
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 Key(Ch const *str, rapidjson::SizeType length, bool copy)
bool Int64(int64 i)
std::vector< std::string > _errors
bool Int(int32 i)
rapidjson::Reader _reader
bool Bool(bool b)
bool EndObject(rapidjson::SizeType memberCount)
std::stack< google::protobuf::Message * > _objectState
bool String(Ch const *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) noexcept
Default TC string format function.