diff options
Diffstat (limited to 'src/google/protobuf/util/internal/protostream_objectwriter.cc')
-rw-r--r-- | src/google/protobuf/util/internal/protostream_objectwriter.cc | 118 |
1 files changed, 76 insertions, 42 deletions
diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.cc b/src/google/protobuf/util/internal/protostream_objectwriter.cc index 2edfd075..9c52116e 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.cc +++ b/src/google/protobuf/util/internal/protostream_objectwriter.cc @@ -32,28 +32,33 @@ #include <functional> #include <stack> +#include <unordered_map> +#include <unordered_set> -#include <google/protobuf/stubs/once.h> #include <google/protobuf/stubs/time.h> +#include <google/protobuf/stubs/once.h> #include <google/protobuf/wire_format_lite.h> #include <google/protobuf/util/internal/field_mask_utility.h> #include <google/protobuf/util/internal/object_location_tracker.h> #include <google/protobuf/util/internal/constants.h> #include <google/protobuf/util/internal/utility.h> #include <google/protobuf/stubs/strutil.h> + #include <google/protobuf/stubs/map_util.h> #include <google/protobuf/stubs/statusor.h> +#include <google/protobuf/port_def.inc> + namespace google { namespace protobuf { namespace util { namespace converter { -using google::protobuf::internal::WireFormatLite; -using util::error::INVALID_ARGUMENT; +using ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite; using util::Status; using util::StatusOr; +using util::error::INVALID_ARGUMENT; ProtoStreamObjectWriter::ProtoStreamObjectWriter( @@ -65,11 +70,24 @@ ProtoStreamObjectWriter::ProtoStreamObjectWriter( current_(nullptr), options_(options) { set_ignore_unknown_fields(options_.ignore_unknown_fields); + set_ignore_unknown_enum_values(options_.ignore_unknown_enum_values); set_use_lower_camel_for_enums(options_.use_lower_camel_for_enums); } ProtoStreamObjectWriter::ProtoStreamObjectWriter( const TypeInfo* typeinfo, const google::protobuf::Type& type, + strings::ByteSink* output, ErrorListener* listener, + const ProtoStreamObjectWriter::Options& options) + : ProtoWriter(typeinfo, type, output, listener), + master_type_(type), + current_(nullptr), + options_(options) { + set_ignore_unknown_fields(options_.ignore_unknown_fields); + set_use_lower_camel_for_enums(options.use_lower_camel_for_enums); +} + +ProtoStreamObjectWriter::ProtoStreamObjectWriter( + const TypeInfo* typeinfo, const google::protobuf::Type& type, strings::ByteSink* output, ErrorListener* listener) : ProtoWriter(typeinfo, type, output, listener), master_type_(type), @@ -120,7 +138,7 @@ Status GetNanosFromStringPiece(StringPiece s_nanos, // "0." + s_nanos.ToString() seconds. An int32 is used for the // conversion to 'nanos', rather than a double, so that there is no // loss of precision. - if (!s_nanos.empty() && !safe_strto32(s_nanos.ToString(), &i_nanos)) { + if (!s_nanos.empty() && !safe_strto32(s_nanos, &i_nanos)) { return Status(INVALID_ARGUMENT, parse_failure_message); } if (i_nanos > kNanosPerSecond || i_nanos < 0) { @@ -305,7 +323,7 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) { // Figure out the type url. This is a copy-paste from WriteString but we also // need the value, so we can't just call through to that. if (value.type() == DataPiece::TYPE_STRING) { - type_url_ = value.str().ToString(); + type_url_ = string(value.str()); } else { StatusOr<string> s = value.ToString(); if (!s.ok()) { @@ -337,7 +355,7 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) { // Create our object writer and initialize it with the first StartObject // call. ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo(), *type, &output_, - parent_->listener())); + parent_->listener(), parent_->options_)); // Don't call StartObject() for well-known types yet. Depending on the // type of actual data, we may not need to call StartObject(). For @@ -368,8 +386,9 @@ void ProtoStreamObjectWriter::AnyWriter::WriteAny() { } else { // There are uninterpreted data, but we never got a "@type" field. if (!invalid_) { - parent_->InvalidValue("Any", StrCat("Missing @type for any field in ", - parent_->master_type_.name())); + parent_->InvalidValue("Any", + StrCat("Missing @type for any field in ", + parent_->master_type_.name())); invalid_ = true; } return; @@ -431,7 +450,7 @@ ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter* enclosing, any_.reset(new AnyWriter(ow_)); } if (item_type == MAP) { - map_keys_.reset(new hash_set<string>); + map_keys_.reset(new std::unordered_set<string>); } } @@ -448,13 +467,13 @@ ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter::Item* parent, any_.reset(new AnyWriter(ow_)); } if (item_type == MAP) { - map_keys_.reset(new hash_set<string>); + map_keys_.reset(new std::unordered_set<string>); } } bool ProtoStreamObjectWriter::Item::InsertMapKeyIfNotPresent( StringPiece map_key) { - return InsertIfNotPresent(map_keys_.get(), map_key.ToString()); + return InsertIfNotPresent(map_keys_.get(), string(map_key)); } ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject( @@ -477,7 +496,7 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject( // stream, we write those values. if (master_type_.name() == kStructType) { // Struct has a map<string, Value> field called "fields". - // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto + // https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/struct.proto // "fields": [ Push("fields", Item::MAP, true, true); return this; @@ -488,7 +507,7 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject( // object within that type is a struct type. So start a struct. // // The struct field in Value type is named "struct_value" - // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto + // https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/struct.proto // Also start the map field "fields" within the struct. // "struct_value": { // "fields": [ @@ -534,7 +553,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject( Push("", Item::MESSAGE, false, false); ProtoWriter::RenderDataPiece("key", DataPiece(name, use_strict_base64_decoding())); - Push("value", Item::MESSAGE, true, false); + Push("value", IsAny(*Lookup("value")) ? Item::ANY : Item::MESSAGE, true, + false); // Make sure we are valid so far after starting map fields. if (invalid_depth() > 0) return this; @@ -618,7 +638,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndObject() { return this; } -ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) { +ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList( + StringPiece name) { if (invalid_depth() > 0) { IncrementInvalidDepth(); return this; @@ -641,7 +662,7 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) { // we have to start the "list_value" within google.protobuf.Value. // // See - // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto + // https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/struct.proto // // Render // "<name>": { @@ -727,7 +748,7 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) { // Report an error. InvalidValue("Map", StrCat("Cannot have repeated items ('", name, - "') within a map.")); + "') within a map.")); return this; } @@ -818,8 +839,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) { } if (IsMap(*field)) { - InvalidValue("Map", - StrCat("Cannot bind a list to map for field '", name, "'.")); + InvalidValue("Map", StrCat("Cannot bind a list to map for field '", + name, "'.")); IncrementInvalidDepth(); return this; } @@ -924,7 +945,7 @@ Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow, if (data.type() != DataPiece::TYPE_STRING) { return Status(INVALID_ARGUMENT, StrCat("Invalid data type for timestamp, value is ", - data.ValueAsStringOrDefault(""))); + data.ValueAsStringOrDefault(""))); } StringPiece value(data.str()); @@ -955,13 +976,13 @@ Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow, if (data.type() != DataPiece::TYPE_STRING) { return Status(INVALID_ARGUMENT, StrCat("Invalid data type for field mask, value is ", - data.ValueAsStringOrDefault(""))); + data.ValueAsStringOrDefault(""))); } // TODO(tsun): figure out how to do proto descriptor based snake case // conversions as much as possible. Because ToSnakeCase sometimes returns the // wrong value. - std::unique_ptr<ResultCallback1<util::Status, StringPiece> > callback( + std::unique_ptr<ResultCallback1<util::Status, StringPiece>> callback( ::google::protobuf::NewPermanentCallback(&RenderOneFieldPath, ow)); return DecodeCompactFieldMaskPaths(data.str(), callback.get()); } @@ -972,7 +993,7 @@ Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow, if (data.type() != DataPiece::TYPE_STRING) { return Status(INVALID_ARGUMENT, StrCat("Invalid data type for duration, value is ", - data.ValueAsStringOrDefault(""))); + data.ValueAsStringOrDefault(""))); } StringPiece value(data.str()); @@ -1042,8 +1063,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( ProtoWriter::StartObject(name); status = (*type_renderer)(this, data); if (!status.ok()) { - InvalidValue(master_type_.name(), - StrCat("Field '", name, "', ", status.error_message())); + InvalidValue(master_type_.name(), StrCat("Field '", name, "', ", + status.error_message())); } ProtoWriter::EndObject(); return this; @@ -1058,18 +1079,27 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( if (current_->IsMap()) { if (!ValidMapKey(name)) return this; - // Render an item in repeated map list. - // { "key": "<name>", "value": - Push("", Item::MESSAGE, false, false); - ProtoWriter::RenderDataPiece("key", - DataPiece(name, use_strict_base64_decoding())); field = Lookup("value"); if (field == nullptr) { - Pop(); GOOGLE_LOG(DFATAL) << "Map does not have a value field."; return this; } + if (options_.ignore_null_value_map_entry) { + // If we are rendering explicit null values and the backend proto field is + // not of the google.protobuf.NullType type, interpret null as absence. + if (data.type() == DataPiece::TYPE_NULL && + field->type_url() != kStructNullValueTypeUrl) { + return this; + } + } + + // Render an item in repeated map list. + // { "key": "<name>", "value": + Push("", Item::MESSAGE, false, false); + ProtoWriter::RenderDataPiece("key", + DataPiece(name, use_strict_base64_decoding())); + const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url()); if (type_renderer != nullptr) { // Map's value type is a special type. Render it like a message: @@ -1079,8 +1109,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( Push("value", Item::MESSAGE, true, false); status = (*type_renderer)(this, data); if (!status.ok()) { - InvalidValue(field->type_url(), - StrCat("Field '", name, "', ", status.error_message())); + InvalidValue(field->type_url(), StrCat("Field '", name, "', ", + status.error_message())); } Pop(); return this; @@ -1113,8 +1143,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( Push(name, Item::MESSAGE, false, false); status = (*type_renderer)(this, data); if (!status.ok()) { - InvalidValue(field->type_url(), - StrCat("Field '", name, "', ", status.error_message())); + InvalidValue(field->type_url(), StrCat("Field '", name, "', ", + status.error_message())); } Pop(); } @@ -1134,12 +1164,13 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( // Map of functions that are responsible for rendering well known type // represented by the key. -hash_map<string, ProtoStreamObjectWriter::TypeRenderer>* +std::unordered_map<string, ProtoStreamObjectWriter::TypeRenderer>* ProtoStreamObjectWriter::renderers_ = NULL; -GOOGLE_PROTOBUF_DECLARE_ONCE(writer_renderers_init_); +PROTOBUF_NAMESPACE_ID::internal::once_flag writer_renderers_init_; void ProtoStreamObjectWriter::InitRendererMap() { - renderers_ = new hash_map<string, ProtoStreamObjectWriter::TypeRenderer>(); + renderers_ = + new std::unordered_map<string, ProtoStreamObjectWriter::TypeRenderer>(); (*renderers_)["type.googleapis.com/google.protobuf.Timestamp"] = &ProtoStreamObjectWriter::RenderTimestamp; (*renderers_)["type.googleapis.com/google.protobuf.Duration"] = @@ -1194,7 +1225,8 @@ void ProtoStreamObjectWriter::DeleteRendererMap() { ProtoStreamObjectWriter::TypeRenderer* ProtoStreamObjectWriter::FindTypeRenderer(const string& type_url) { - ::google::protobuf::GoogleOnceInit(&writer_renderers_init_, &InitRendererMap); + PROTOBUF_NAMESPACE_ID::internal::call_once(writer_renderers_init_, + InitRendererMap); return FindOrNull(*renderers_, type_url); } @@ -1204,14 +1236,16 @@ bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) { if (!current_->InsertMapKeyIfNotPresent(unnormalized_name)) { listener()->InvalidName( location(), unnormalized_name, - StrCat("Repeated map key: '", unnormalized_name, "' is already set.")); + StrCat("Repeated map key: '", unnormalized_name, + "' is already set.")); return false; } return true; } -void ProtoStreamObjectWriter::Push(StringPiece name, Item::ItemType item_type, +void ProtoStreamObjectWriter::Push(StringPiece name, + Item::ItemType item_type, bool is_placeholder, bool is_list) { is_list ? ProtoWriter::StartList(name) : ProtoWriter::StartObject(name); @@ -1247,7 +1281,7 @@ bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) { const google::protobuf::Type* field_type = typeinfo()->GetTypeByTypeUrl(field.type_url()); - return google::protobuf::util::converter::IsMap(field, *field_type); + return converter::IsMap(field, *field_type); } bool ProtoStreamObjectWriter::IsAny(const google::protobuf::Field& field) { |