Index: third_party/protobuf/src/google/protobuf/util/internal/protostream_objectwriter.cc |
diff --git a/third_party/protobuf/src/google/protobuf/util/internal/protostream_objectwriter.cc b/third_party/protobuf/src/google/protobuf/util/internal/protostream_objectwriter.cc |
index 786bf0be20d6446bc5767dac56c3dd6e30ab6f10..97a7909a185df2f61ac721b50109b57b21ef12c8 100644 |
--- a/third_party/protobuf/src/google/protobuf/util/internal/protostream_objectwriter.cc |
+++ b/third_party/protobuf/src/google/protobuf/util/internal/protostream_objectwriter.cc |
@@ -58,17 +58,20 @@ using util::StatusOr; |
ProtoStreamObjectWriter::ProtoStreamObjectWriter( |
TypeResolver* type_resolver, const google::protobuf::Type& type, |
- strings::ByteSink* output, ErrorListener* listener) |
+ strings::ByteSink* output, ErrorListener* listener, |
+ const ProtoStreamObjectWriter::Options& options) |
: ProtoWriter(type_resolver, type, output, listener), |
master_type_(type), |
- current_(NULL) {} |
+ current_(NULL), |
+ options_(options) {} |
ProtoStreamObjectWriter::ProtoStreamObjectWriter( |
const TypeInfo* typeinfo, const google::protobuf::Type& type, |
strings::ByteSink* output, ErrorListener* listener) |
: ProtoWriter(typeinfo, type, output, listener), |
master_type_(type), |
- current_(NULL) {} |
+ current_(NULL), |
+ options_(ProtoStreamObjectWriter::Options::Defaults()) {} |
ProtoStreamObjectWriter::~ProtoStreamObjectWriter() { |
if (current_ == NULL) return; |
@@ -179,7 +182,8 @@ ProtoStreamObjectWriter::AnyWriter::AnyWriter(ProtoStreamObjectWriter* parent) |
data_(), |
output_(&data_), |
depth_(0), |
- has_injected_value_message_(false) {} |
+ is_well_known_type_(false), |
+ well_known_type_render_(NULL) {} |
ProtoStreamObjectWriter::AnyWriter::~AnyWriter() {} |
@@ -197,10 +201,19 @@ void ProtoStreamObjectWriter::AnyWriter::StartObject(StringPiece name) { |
parent_->master_type_.name())); |
invalid_ = true; |
} |
- } else if (!has_injected_value_message_ || depth_ != 1 || name != "value") { |
- // We don't propagate to ow_ StartObject("value") calls for nested Anys or |
- // Struct at depth 1 as they are nested one level deep with an injected |
+ } else if (is_well_known_type_ && depth_ == 1) { |
+ // For well-known types, the only other field besides "@type" should be a |
// "value" field. |
+ if (name != "value" && !invalid_) { |
+ parent_->InvalidValue("Any", |
+ "Expect a \"value\" field for well-known types."); |
+ invalid_ = true; |
+ } |
+ ow_->StartObject(""); |
+ } else { |
+ // Forward the call to the child writer if: |
+ // 1. the type is not a well-known type. |
+ // 2. or, we are in a nested Any, Struct, or Value object. |
ow_->StartObject(name); |
} |
} |
@@ -208,10 +221,9 @@ void ProtoStreamObjectWriter::AnyWriter::StartObject(StringPiece name) { |
bool ProtoStreamObjectWriter::AnyWriter::EndObject() { |
--depth_; |
// As long as depth_ >= 0, we know we haven't reached the end of Any. |
- // Propagate these EndObject() calls to the contained ow_. If we are in a |
- // nested Any or Struct type, ignore the second to last EndObject call (depth_ |
- // == -1) |
- if (ow_ != NULL && (!has_injected_value_message_ || depth_ >= 0)) { |
+ // Propagate these EndObject() calls to the contained ow_. For regular |
+ // message types, we propagate the end of Any as well. |
+ if (ow_ != NULL && (depth_ >= 0 || !is_well_known_type_)) { |
ow_->EndObject(); |
} |
// A negative depth_ implies that we have reached the end of Any |
@@ -233,6 +245,13 @@ void ProtoStreamObjectWriter::AnyWriter::StartList(StringPiece name) { |
parent_->master_type_.name())); |
invalid_ = true; |
} |
+ } else if (is_well_known_type_ && depth_ == 1) { |
+ if (name != "value" && !invalid_) { |
+ parent_->InvalidValue("Any", |
+ "Expect a \"value\" field for well-known types."); |
+ invalid_ = true; |
+ } |
+ ow_->StartList(""); |
} else { |
ow_->StartList(name); |
} |
@@ -263,17 +282,27 @@ void ProtoStreamObjectWriter::AnyWriter::RenderDataPiece( |
parent_->master_type_.name())); |
invalid_ = true; |
} |
- } else { |
- // Check to see if the data needs to be rendered with well-known-type |
- // renderer. |
- const TypeRenderer* type_renderer = |
- FindTypeRenderer(GetFullTypeWithUrl(ow_->master_type_.name())); |
- if (type_renderer) { |
- Status status = (*type_renderer)(ow_.get(), value); |
- if (!status.ok()) ow_->InvalidValue("Any", status.error_message()); |
+ } else if (depth_ == 0 && is_well_known_type_) { |
+ if (name != "value" && !invalid_) { |
+ parent_->InvalidValue("Any", |
+ "Expect a \"value\" field for well-known types."); |
+ invalid_ = true; |
+ } |
+ if (well_known_type_render_ == NULL) { |
+ // Only Any and Struct don't have a special type render but both of |
+ // them expect a JSON object (i.e., a StartObject() call). |
+ if (!invalid_) { |
+ parent_->InvalidValue("Any", "Expect a JSON object."); |
+ invalid_ = true; |
+ } |
} else { |
- ow_->RenderDataPiece(name, value); |
+ ow_->ProtoWriter::StartObject(""); |
+ Status status = (*well_known_type_render_)(ow_.get(), value); |
+ if (!status.ok()) ow_->InvalidValue("Any", status.error_message()); |
+ ow_->ProtoWriter::EndObject(); |
} |
+ } else { |
+ ow_->RenderDataPiece(name, value); |
} |
} |
@@ -302,19 +331,31 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) { |
// At this point, type is never null. |
const google::protobuf::Type* type = resolved_type.ValueOrDie(); |
- // If this is the case of an Any in an Any or Struct in an Any, we need to |
- // expect a StartObject call with "value" while we're at depth_ 0, which we |
- // should ignore (not propagate to our nested object writer). We also need to |
- // ignore the second-to-last EndObject call, and not propagate that either. |
- if (type->name() == kAnyType || type->name() == kStructType) { |
- has_injected_value_message_ = true; |
+ well_known_type_render_ = FindTypeRenderer(type_url_); |
+ if (well_known_type_render_ != NULL || |
+ // Explicitly list Any and Struct here because they don't have a |
+ // custom renderer. |
+ type->name() == kAnyType || type->name() == kStructType) { |
+ is_well_known_type_ = true; |
} |
// Create our object writer and initialize it with the first StartObject |
// call. |
ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo(), *type, &output_, |
parent_->listener())); |
- ow_->StartObject(""); |
+ |
+ // Don't call StartObject() for well-known types yet. Depending on the |
+ // type of actual data, we may not need to call StartObject(). For |
+ // example: |
+ // { |
+ // "@type": "type.googleapis.com/google.protobuf.Value", |
+ // "value": [1, 2, 3], |
+ // } |
+ // With the above JSON representation, we will only call StartList() on the |
+ // contained ow_. |
+ if (!is_well_known_type_) { |
+ ow_->StartObject(""); |
+ } |
} |
void ProtoStreamObjectWriter::AnyWriter::WriteAny() { |
@@ -439,7 +480,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject( |
// name): |
// { "key": "<name>", "value": { |
Push("", Item::MESSAGE, false, false); |
- ProtoWriter::RenderDataPiece("key", DataPiece(name)); |
+ ProtoWriter::RenderDataPiece("key", |
+ DataPiece(name, use_strict_base64_decoding())); |
Push("value", Item::MESSAGE, true, false); |
// Make sure we are valid so far after starting map fields. |
@@ -604,7 +646,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) { |
// Render |
// { "key": "<name>", "value": { |
Push("", Item::MESSAGE, false, false); |
- ProtoWriter::RenderDataPiece("key", DataPiece(name)); |
+ ProtoWriter::RenderDataPiece("key", |
+ DataPiece(name, use_strict_base64_decoding())); |
Push("value", Item::MESSAGE, true, false); |
// Make sure we are valid after pushing all above items. |
@@ -758,8 +801,36 @@ Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow, |
string struct_field_name; |
switch (data.type()) { |
// Our JSON parser parses numbers as either int64, uint64, or double. |
- case DataPiece::TYPE_INT64: |
- case DataPiece::TYPE_UINT64: |
+ case DataPiece::TYPE_INT64: { |
+ // If the option to treat integers as strings is set, then render them as |
+ // strings. Otherwise, fallback to rendering them as double. |
+ if (ow->options_.struct_integers_as_strings) { |
+ StatusOr<int64> int_value = data.ToInt64(); |
+ if (int_value.ok()) { |
+ ow->ProtoWriter::RenderDataPiece( |
+ "string_value", |
+ DataPiece(SimpleItoa(int_value.ValueOrDie()), true)); |
+ return Status::OK; |
+ } |
+ } |
+ struct_field_name = "number_value"; |
+ break; |
+ } |
+ case DataPiece::TYPE_UINT64: { |
+ // If the option to treat integers as strings is set, then render them as |
+ // strings. Otherwise, fallback to rendering them as double. |
+ if (ow->options_.struct_integers_as_strings) { |
+ StatusOr<uint64> int_value = data.ToUint64(); |
+ if (int_value.ok()) { |
+ ow->ProtoWriter::RenderDataPiece( |
+ "string_value", |
+ DataPiece(SimpleItoa(int_value.ValueOrDie()), true)); |
+ return Status::OK; |
+ } |
+ } |
+ struct_field_name = "number_value"; |
+ break; |
+ } |
case DataPiece::TYPE_DOUBLE: { |
struct_field_name = "number_value"; |
break; |
@@ -812,7 +883,7 @@ Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow, |
static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow, |
StringPiece path) { |
ow->ProtoWriter::RenderDataPiece( |
- "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase))); |
+ "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase), true)); |
return Status::OK; |
} |
@@ -828,7 +899,7 @@ Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow, |
// conversions as much as possible. Because ToSnakeCase sometimes returns the |
// wrong value. |
google::protobuf::scoped_ptr<ResultCallback1<util::Status, StringPiece> > callback( |
- google::protobuf::internal::NewPermanentCallback(&RenderOneFieldPath, ow)); |
+ ::google::protobuf::internal::NewPermanentCallback(&RenderOneFieldPath, ow)); |
return DecodeCompactFieldMaskPaths(data.str(), callback.get()); |
} |
@@ -871,7 +942,7 @@ Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow, |
nanos = sign * nanos; |
int64 seconds = sign * unsigned_seconds; |
- if (seconds > kMaxSeconds || seconds < kMinSeconds || |
+ if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds || |
nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) { |
return Status(INVALID_ARGUMENT, "Duration value exceeds limits"); |
} |
@@ -925,7 +996,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( |
// Render an item in repeated map list. |
// { "key": "<name>", "value": |
Push("", Item::MESSAGE, false, false); |
- ProtoWriter::RenderDataPiece("key", DataPiece(name)); |
+ ProtoWriter::RenderDataPiece("key", |
+ DataPiece(name, use_strict_base64_decoding())); |
field = Lookup("value"); |
if (field == NULL) { |
GOOGLE_LOG(DFATAL) << "Map does not have a value field."; |
@@ -952,6 +1024,7 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( |
// not of the google.protobuf.NullType type, we do nothing. |
if (data.type() == DataPiece::TYPE_NULL && |
field->type_url() != kStructNullValueTypeUrl) { |
+ Pop(); |
return this; |
} |