aboutsummaryrefslogtreecommitdiff
path: root/src/google/protobuf/util/internal/protostream_objectwriter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/google/protobuf/util/internal/protostream_objectwriter.cc')
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter.cc118
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) {