diff options
Diffstat (limited to 'src/server/shared/JSON')
-rw-r--r-- | src/server/shared/JSON/ProtobufJSON.cpp | 456 | ||||
-rw-r--r-- | src/server/shared/JSON/ProtobufJSON.h | 31 |
2 files changed, 487 insertions, 0 deletions
diff --git a/src/server/shared/JSON/ProtobufJSON.cpp b/src/server/shared/JSON/ProtobufJSON.cpp new file mode 100644 index 00000000000..11108453f06 --- /dev/null +++ b/src/server/shared/JSON/ProtobufJSON.cpp @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ProtobufJSON.h" +#include "StringFormat.h" +#include "Common.h" +#include "Log.h" +#include <rapidjson/writer.h> +#include <rapidjson/reader.h> +#include <rapidjson/stringbuffer.h> +#include <stack> + +class Serializer +{ +public: + Serializer() : _writer(_buffer) { } + + void WriteInt32(int32 value) { _writer.Int(value); } + void WriteInt64(int64 value) { _writer.Int64(value); } + void WriteUInt32(uint32 value) { _writer.Uint(value); } + void WriteUInt64(uint64 value) { _writer.Uint64(value); } + void WriteDouble(double value) { _writer.Double(value); } + void WriteFloat(float value) { _writer.Double(value); } + void WriteBool(bool value) { _writer.Bool(value); } + void WriteEnum(google::protobuf::EnumValueDescriptor const* value) { _writer.String(value->name()); } + void WriteString(std::string const& value) { _writer.String(value); } + void WriteMessage(google::protobuf::Message const& value); + + std::string GetString() const { return std::string(_buffer.GetString(), _buffer.GetSize()); } + +private: + void WriteMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field); + void WriteSimpleMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field); + void WriteRepeatedMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field); + + rapidjson::StringBuffer _buffer; + rapidjson::Writer<rapidjson::StringBuffer> _writer; +}; + +void Serializer::WriteMessage(google::protobuf::Message const& value) +{ + google::protobuf::Reflection const* reflection = value.GetReflection(); + std::vector<google::protobuf::FieldDescriptor const*> fields; + reflection->ListFields(value, &fields); + + _writer.StartObject(); + for (std::size_t i = 0; i < fields.size(); ++i) + WriteMessageField(value, fields[i]); + + _writer.EndObject(); +} + +void Serializer::WriteMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field) +{ + _writer.Key(field->name().c_str()); + if (field->is_repeated()) + { + _writer.StartArray(); + WriteRepeatedMessageField(value, field); + _writer.EndArray(); + } + else + WriteSimpleMessageField(value, field); +} + +void Serializer::WriteSimpleMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field) +{ + google::protobuf::Reflection const* reflection = value.GetReflection(); + switch (field->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: + WriteInt32(reflection->GetInt32(value, field)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: + WriteInt64(reflection->GetInt64(value, field)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + WriteUInt32(reflection->GetUInt32(value, field)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + WriteUInt64(reflection->GetUInt64(value, field)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: + WriteDouble(reflection->GetDouble(value, field)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: + WriteFloat(reflection->GetFloat(value, field)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + WriteBool(reflection->GetBool(value, field)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: + WriteEnum(reflection->GetEnum(value, field)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + std::string strValue = reflection->GetString(value, field); + if (field->type() == google::protobuf::FieldDescriptor::TYPE_STRING) + WriteString(strValue); + else + { + _writer.StartArray(); + for (std::size_t i = 0; i < strValue.length(); ++i) + WriteUInt32(uint32(strValue[i])); + _writer.EndArray(); + } + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: + WriteMessage(reflection->GetMessage(value, field)); + break; + default: + break; + } +} + +void Serializer::WriteRepeatedMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field) +{ + google::protobuf::Reflection const* reflection = value.GetReflection(); + for (int32 i = 0; i < reflection->FieldSize(value, field); ++i) + { + switch (field->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: + WriteInt32(reflection->GetRepeatedInt32(value, field, i)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: + WriteInt64(reflection->GetRepeatedInt64(value, field, i)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + WriteUInt32(reflection->GetRepeatedUInt32(value, field, i)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + WriteUInt64(reflection->GetRepeatedUInt64(value, field, i)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: + WriteDouble(reflection->GetRepeatedDouble(value, field, i)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: + WriteFloat(reflection->GetRepeatedFloat(value, field, i)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + WriteBool(reflection->GetRepeatedBool(value, field, i)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: + WriteEnum(reflection->GetRepeatedEnum(value, field, i)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + std::string strValue = reflection->GetRepeatedString(value, field, i); + if (field->type() == google::protobuf::FieldDescriptor::TYPE_STRING) + WriteString(strValue); + else + { + _writer.StartArray(); + for (std::size_t j = 0; j < strValue.length(); ++j) + WriteUInt32(uint32(strValue[j])); + _writer.EndArray(); + } + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: + WriteMessage(reflection->GetRepeatedMessage(value, field, i)); + break; + default: + break; + } + } +} + +class Deserializer : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, Deserializer> +{ +public: + bool ReadMessage(std::string json, google::protobuf::Message* message); + + bool Key(const Ch* str, rapidjson::SizeType length, bool copy); + bool Null(); + bool Bool(bool b); + bool Int(int32 i); + bool Uint(uint32 i); + bool Int64(int64 i); + bool Uint64(uint64 i); + bool Double(double d); + bool String(const Ch* str, rapidjson::SizeType length, bool copy); + bool StartObject(); + bool EndObject(rapidjson::SizeType memberCount); + bool StartArray(); + bool EndArray(rapidjson::SizeType memberCount); + + std::vector<std::string> const& GetErrors() const { return _errors; } + +private: + bool CheckType(google::protobuf::FieldDescriptor::CppType expectedType); + + rapidjson::Reader _reader; + std::stack<google::protobuf::FieldDescriptor const*> _state; + std::stack<google::protobuf::Message*> _objectState; + std::vector<std::string> _errors; +}; + +bool Deserializer::ReadMessage(std::string json, google::protobuf::Message* message) +{ + rapidjson::StringStream ss(json.c_str()); + + _objectState.push(message); + + rapidjson::ParseResult result = _reader.Parse(ss, *this); + + ASSERT(result.IsError() || (_objectState.empty() && _state.empty())); + + return !result.IsError() && _errors.empty(); +} + +bool Deserializer::Key(const Ch* str, rapidjson::SizeType /*length*/, bool /*copy*/) +{ + google::protobuf::FieldDescriptor const* field = _objectState.top()->GetDescriptor()->FindFieldByName(str); + if (!field) + { + _errors.push_back(Trinity::StringFormat("Message %s has no field %s.", _objectState.top()->GetTypeName().c_str(), str)); + return false; + } + + _state.push(field); + return true; +} + +bool Deserializer::Null() +{ + _state.pop(); + return true; +} + +#define SET_FIELD(message, field, Type, val) do { \ + if (!field->is_repeated()) \ + message->GetReflection()->Set ## Type(message, field, val); \ + else \ + message->GetReflection()->Add ## Type(message, field, val); \ + _state.pop(); \ + } while (0) + +bool Deserializer::Bool(bool b) +{ + if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_BOOL)) + return false; + + SET_FIELD(_objectState.top(), _state.top(), Bool, b); + return true; +} + +bool Deserializer::Int(int32 i) +{ + if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_INT32)) + return false; + + SET_FIELD(_objectState.top(), _state.top(), Int32, i); + return true; +} + +bool Deserializer::Uint(uint32 i) +{ + google::protobuf::FieldDescriptor const* field = _state.top(); + google::protobuf::Message* message = _objectState.top(); + switch (field->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + SET_FIELD(message, field, UInt32, i); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + { + if (field->type() != google::protobuf::FieldDescriptor::TYPE_BYTES) + { + _errors.emplace_back("Expected field type to be bytes but got string instead."); + return false; + } + std::string currentValue = message->GetReflection()->GetString(*message, field); + currentValue.append(1, (char)i); + message->GetReflection()->SetString(message, field, currentValue); + break; + } + default: + _errors.push_back(Trinity::StringFormat("Expected field type to be uint32 or string but got %s instead.", _state.top()->cpp_type_name())); + return false; + } + + return true; +} + +bool Deserializer::Int64(int64 i) +{ + if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_INT64)) + return false; + + SET_FIELD(_objectState.top(), _state.top(), Int64, i); + return true; +} + +bool Deserializer::Uint64(uint64 i) +{ + if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_UINT64)) + return false; + + SET_FIELD(_objectState.top(), _state.top(), UInt64, i); + return true; +} + +bool Deserializer::Double(double d) +{ + google::protobuf::FieldDescriptor const* field = _state.top(); + google::protobuf::Message* message = _objectState.top(); + switch (field->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: + SET_FIELD(message, field, Float, d); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: + SET_FIELD(message, field, Double, d); + break; + default: + _errors.push_back(Trinity::StringFormat("Expected field type to be float or double but got %s instead.", _state.top()->cpp_type_name())); + return false; + } + + return true; +} + +bool Deserializer::String(const Ch* str, rapidjson::SizeType /*length*/, bool /*copy*/) +{ + google::protobuf::FieldDescriptor const* field = _state.top(); + google::protobuf::Message* message = _objectState.top(); + switch (field->cpp_type()) + { + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: + { + google::protobuf::EnumValueDescriptor const* enumValue = field->enum_type()->FindValueByName(str); + if (!enumValue) + { + _errors.push_back(Trinity::StringFormat("Field %s enum %s does not have a value named %s.", field->full_name().c_str(), field->enum_type()->full_name().c_str(), str)); + return false; + } + + SET_FIELD(message, field, Enum, enumValue); + break; + } + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + SET_FIELD(message, field, String, str); + break; + default: + _errors.push_back(Trinity::StringFormat("Expected field type to be string or enum but got %s instead.", _state.top()->cpp_type_name())); + return false; + } + + return true; +} + +bool Deserializer::StartObject() +{ + // not a root object + if (!_state.empty()) + { + if (_state.top()->cpp_type() != google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) + { + _errors.push_back(Trinity::StringFormat("Expected field %s to be a message but got %s instead.", _state.top()->cpp_type_name())); + return false; + } + + google::protobuf::Message* containingMessage = _objectState.top(); + if (!_state.top()->is_repeated()) + _objectState.push(containingMessage->GetReflection()->MutableMessage(containingMessage, _state.top())); + else + _objectState.push(containingMessage->GetReflection()->AddMessage(containingMessage, _state.top())); + } + else if (_objectState.size() != 1) + return false; + + return true; +} + +bool Deserializer::EndObject(rapidjson::SizeType /*memberCount*/) +{ + if (!_state.empty() && !_state.top()->is_repeated()) + _state.pop(); + + _objectState.pop(); + return true; +} + +bool Deserializer::StartArray() +{ + if (_state.empty()) + { + _errors.emplace_back("Root cannot be an array."); + return false; + } + + if (_state.top()->is_repeated() ^ (_state.top()->type() != google::protobuf::FieldDescriptor::TYPE_BYTES)) + { + _errors.push_back(Trinity::StringFormat("Expected field %s type to be exactly an array OR bytes but it was both or none.", _state.top()->full_name().c_str())); + return false; + } + + return true; +} + +bool Deserializer::CheckType(google::protobuf::FieldDescriptor::CppType expectedType) +{ + if (_state.top()->cpp_type() != expectedType) + { + _errors.push_back(Trinity::StringFormat("Expected field %s type to be %s but got %s instead.", + _state.top()->full_name().c_str(), google::protobuf::FieldDescriptor::CppTypeName(expectedType), _state.top()->cpp_type_name())); + return false; + } + + return true; +} + +bool Deserializer::EndArray(rapidjson::SizeType /*memberCount*/) +{ + _state.pop(); + return true; +} + +#undef SET_FIELD + +std::string JSON::Serialize(google::protobuf::Message const& message) +{ + Serializer serializer; + serializer.WriteMessage(message); + return serializer.GetString(); +} + +bool JSON::Deserialize(std::string json, google::protobuf::Message* message) +{ + Deserializer deserializer; + if (!deserializer.ReadMessage(std::forward<std::string>(json), message)) + { + for (std::size_t i = 0; i < deserializer.GetErrors().size(); ++i) + TC_LOG_ERROR("json", "%s", deserializer.GetErrors()[i].c_str()); + return false; + } + + return true; +} diff --git a/src/server/shared/JSON/ProtobufJSON.h b/src/server/shared/JSON/ProtobufJSON.h new file mode 100644 index 00000000000..5ddc09bb474 --- /dev/null +++ b/src/server/shared/JSON/ProtobufJSON.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ProtobufJSON_h__ +#define ProtobufJSON_h__ + +#include "Define.h" +#include <google/protobuf/message.h> +#include <string> + +namespace JSON +{ + TC_SHARED_API std::string Serialize(google::protobuf::Message const& message); + TC_SHARED_API bool Deserialize(std::string json, google::protobuf::Message* message); +} + +#endif // ProtobufJSON_h__ |