> On March 1, 2018, 12:10 p.m., Benjamin Mahler wrote: > > 3rdparty/stout/include/stout/protobuf.hpp > > Lines 391-487 (patched) > > <https://reviews.apache.org/r/59987/diff/5/?file=1965083#file1965083line391> > > > > It looks like we can avoid this logic since protobuf's JSON conversion > > allows the protobuf key types (bool, integers, strings) to be converted > > from JSON strings. This means we could just recurse for both key and value > > here: > > > > ``` > > // Some comment explaining what map is equivalent to with > > // a reference to the google documentation. > > google::protobuf::Message* entry = > > reflection->AddMessage(message, field); > > > > const google::protobuf::FieldDescriptor* key_field = > > entry->GetDescriptor()->FindFieldByNumber(1); > > > > // Maybe passing just 'key' works due to implicit > > construction? > > // TODO(...): This copies the key, consider avoiding this. > > Try<Nothing> apply = > > boost::apply_visitor(Parser(entry, key_field), > > JSON::String(key)); > > > > if (apply.isError()) { > > return Error(apply.error()); > > } > > > > const google::protobuf::FieldDescriptor* value_field = > > entry->GetDescriptor()->FindFieldByNumber(2); > > > > Try<Nothing> apply = > > boost::apply_visitor(Parser(entry, value_field), value); > > > > if (apply.isError()) { > > return Error(apply.error()); > > } > > ``` > > > > Now, to make this simplification work, we need to update our JSON > > conversion in a separate patch to allow bools and integers to be parsed > > from JSON strings to match google's logic for conversion: > > > > > > https://github.com/google/protobuf/blob/v3.5.1/src/google/protobuf/util/internal/datapiece.cc#L203 > > > > Documentation here: > > https://developers.google.com/protocol-buffers/docs/proto3#json > > Qian Zhang wrote: > > // Maybe passing just 'key' works due to implicit construction? > > We cannot pass `key` or even `JSON::String` to `boost::apply_visitor()` > because it cannot pass compilation: > ``` > > ../3rdparty/boost-1.65.0/boost/variant/detail/apply_visitor_unary.hpp:84:22: > error: no member named 'apply_visitor' in 'std::__1::basic_string<char>' > ``` > or > ``` > > ../3rdparty/boost-1.65.0/boost/variant/detail/apply_visitor_unary.hpp:84:22: > error: no member named 'apply_visitor' in 'JSON::String' > ``` > That is because `std::string` and `JSON::String` do not have a member > `apply_visitor`, so I think we have to use `JSON::Value` like this: > ``` > google::protobuf::Message* entry = > reflection->AddMessage(message, field); > > const google::protobuf::FieldDescriptor* key_field = > entry->GetDescriptor()->FindFieldByNumber(1); > > JSON::Value key(name); > > Try<Nothing> apply = > boost::apply_visitor(Parser(entry, key_field), key); > > if (apply.isError()) { > return Error(apply.error()); > } > > const google::protobuf::FieldDescriptor* value_field = > entry->GetDescriptor()->FindFieldByNumber(2); > > apply = boost::apply_visitor(Parser(entry, value_field), > value); > if (apply.isError()) { > return Error(apply.error()); > } > ``` > > > we need to update our JSON conversion in a separate patch to allow > bools and integers to be parsed from JSON strings to match google's logic for > conversion > > Did you mean we should improve the method below by adding more cases for > converting `JSON::String` to bools and integers? > ``` > Try<Nothing> operator()(const JSON::String& string) const > { > switch (field->type()) { > case google::protobuf::FieldDescriptor::TYPE_STRING: > ... > ``` > If so, then that means moving the code here (see below) into the above > method. But I think those code are specific for map support, however > `Try<Nothing> operator()(const JSON::String& string) const` is a generic > method for JSON string conversion, so I would still like to keep those code > as where they are now in this patch (i.e., the code path to handle map). > ``` > case google::protobuf::FieldDescriptor::TYPE_INT64: > case google::protobuf::FieldDescriptor::TYPE_SINT64: > case google::protobuf::FieldDescriptor::TYPE_SFIXED64: > { > Try<int64_t> key = numify<int64_t>(name); > ... > } > case google::protobuf::FieldDescriptor::TYPE_UINT64: > case google::protobuf::FieldDescriptor::TYPE_FIXED64: > { > Try<uint64_t> key = numify<uint64_t>(name); > ... > } > ... > ``` > > Chun-Hung Hsiao wrote: > Re: `JSON::Value`: It seems that there's no need to declare a new `key` > variable, and just pass `JSON::Value(name)` into `apply_visitor`. > > Re: `JSON::String` <-> bools and ints: Yes. Ben's actually suggesting > that we should allow this conversion in general, an an enhancement of stout's > JSON support, so these conversions need not to be specific for map support. > For example, if we implement this enhancement, we could parse JSON outputed > from Google's JSON function. You could probably prepend another patch for > this enhancement.
> It seems that there's no need to declare a new key variable, and just pass > JSON::Value(name) into apply_visitor. I tried it before, but it can not pass the compilation. ``` ../../3rdparty/stout/include/stout/protobuf.hpp:408:15: error: no matching function for call to 'apply_visitor' boost::apply_visitor(Parser(entry, key_field), JSON::Value(name)); ^~~~~~~~~~~~~~~~~~~~ ../3rdparty/boost-1.65.0/boost/variant/detail/apply_visitor_unary.hpp:82:1: note: candidate function [with Visitor = protobuf::internal::Parser, Visitable = JSON::Value] not viable: expects an l-value for 2nd argument apply_visitor(const Visitor& visitor, Visitable& visitable) ``` > Re: JSON::String <-> bools and ints: Yes. Ben's actually suggesting that we > should allow this conversion in general, an an enhancement of stout's JSON > support, so these conversions need not to be specific for map support. For > example, if we implement this enhancement, we could parse JSON outputed from > Google's JSON function. You could probably prepend another patch for this > enhancement. OK, so we need this enhancement to make our stout JSON -> protobuf conversion can handle more valid JSONs. The followings are valid in JSON which can be successfully parsed by Google's protobuf utility function `JsonStringToMessage()` but can NOT be parsed our stout (i.e., call `JSON::parse()` to convert the JSON to a stout JSON object and then call `protobuf::parse()` to parse the JSON object to a protobuf message). ``` "int32": "-2147483647" "int64": "-9223372036854775807" "bool": "true" ``` The second step (`protobuf::parse()`) will fail with an error like this: ``` Not expecting a JSON string for field 'int32' ``` This error comes from `Try<Nothing> operator()(const JSON::String& string)`, so we need to enhance it to convert `JSON::String` to bools and integers. I created a separate ticket (https://issues.apache.org/jira/browse/MESOS-8656) for it, let's handle it there. > On March 1, 2018, 12:10 p.m., Benjamin Mahler wrote: > > 3rdparty/stout/include/stout/protobuf.hpp > > Lines 400-401 (patched) > > <https://reviews.apache.org/r/59987/diff/5/?file=1965083#file1965083line400> > > > > Can you move this out of the loop? > > Qian Zhang wrote: > It needs the variable `entry` which is defined inside of the > `foreachpair` loop, so I think we still need to keep `reflection` inside of > the loop. > > Chun-Hung Hsiao wrote: > The `entry` itself doesn't depend on `name` and `value`, so let's hoist > them outside the loop. We get `entry` by calling `AddMessage()`: ``` google::protobuf::Message* entry = reflection->AddMessage(message, field); ``` I think we need to call `AddMessage` inside of the `foreachpair` loop rather than outside. > On March 1, 2018, 12:10 p.m., Benjamin Mahler wrote: > > 3rdparty/stout/include/stout/protobuf.hpp > > Lines 1039-1043 (patched) > > <https://reviews.apache.org/r/59987/diff/5/?file=1965083#file1965083line1040> > > > > Is there a way to abstract out this logic into a lambda here and re-use > > it? E.g. > > > > ``` > > template <typename T> > > JSON::Value value_for_field( > > Message* entry, > > google::protobuf::Reflection* reflection, > > google::protobuf::FieldDescriptor* field) > > { > > switch (type) { > > case ...: > > case TYPE_INT64: > > return ...; > > } > > }; > > ``` > > > > Then you just do the following here and elsewhere: > > > > ``` > > map.values[key] = value_for_field(entry, reflection, field); > > ``` > > > > I've glossed over some details, but I hope you get the idea and can > > figure out how to make it work across the different cases here. > > Qian Zhang wrote: > Do you mean moving the big `"switch ... case"` introduced in my patch for > map into a method `value_for_field()`, and use it to replace the other two > big `"switch ... case"` for array and object (see below) too? > > https://github.com/apache/mesos/blob/1.5.0/3rdparty/stout/include/stout/protobuf.hpp#L864:L923 > > https://github.com/apache/mesos/blob/1.5.0/3rdparty/stout/include/stout/protobuf.hpp#L927:L986 > > I like the idea and I think it is doable for map and object, but for > array, it is a bit different since we will call different protobuf APIs, > e.g., `reflection->GetInt32` (map and object) v.s. > `reflection->GetRepeatedInt32()` (array). So I think it may not be able to > cover array :-( > > Chun-Hung Hsiao wrote: > Let's do it for maps and objects. It's unfortunate but seems we have to > keep the array case as is. OK - Qian ----------------------------------------------------------- This is an automatically generated e-mail. To reply, visit: https://reviews.apache.org/r/59987/#review198429 ----------------------------------------------------------- On March 6, 2018, 5:29 p.m., Qian Zhang wrote: > > ----------------------------------------------------------- > This is an automatically generated e-mail. To reply, visit: > https://reviews.apache.org/r/59987/ > ----------------------------------------------------------- > > (Updated March 6, 2018, 5:29 p.m.) > > > Review request for mesos, Anand Mazumdar, Benjamin Mahler, Chun-Hung Hsiao, > and Zhitao Li. > > > Bugs: MESOS-7656 > https://issues.apache.org/jira/browse/MESOS-7656 > > > Repository: mesos > > > Description > ------- > > Map is a feature of proto2 syntax, but it can only be compiled > with 3.0.0+ protobuf compiler, see the following discussion for > details: > > https://groups.google.com/forum/#!topic/protobuf/p4WxcplrlA4 > > We have already upgraded the compiler from 2.6.1 to 3.3.0 in > MESOS-7228. This patch adds map support in the json <-> protobuf > conversion in stout. > > > Diffs > ----- > > 3rdparty/stout/include/stout/protobuf.hpp > 4a1605e5130dbf7e8286dbb43d0d04ab4394e79a > > > Diff: https://reviews.apache.org/r/59987/diff/6/ > > > Testing > ------- > > > Thanks, > > Qian Zhang > >