> 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
> 
>

Reply via email to