Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

merge: handle reflect.Array #146

Closed
wants to merge 2 commits into from
Closed

merge: handle reflect.Array #146

wants to merge 2 commits into from

Conversation

tamird
Copy link
Contributor

@tamird tamird commented Feb 11, 2016

@awalterschulze this currently falls down if you use a customtype which contains an array.

@tamird
Copy link
Contributor Author

tamird commented Feb 11, 2016

This isn't totally right yet.

@awalterschulze
Copy link
Member

gogoprotobuf does not currently support Clone
#14
There is no reason that I can immediately think of that it can't support Clone.
I just think it will take a bit of effort to fully support. Mostly just a lot of testing.

But I guess we can start with a test for something that does not work and a fix for that specific issue.
A small improvement in this case should also be a fine contribution.

@tamird
Copy link
Contributor Author

tamird commented Feb 11, 2016

@awalterschulze ok, so you need me to add a test? Can do.

@tamird
Copy link
Contributor Author

tamird commented Feb 11, 2016

@awalterschulze I changed the Uuid type to a [16]byte to try to test this, but it turns out that causes a huge number of panics. Could you take a look? Your help is greatly appreciated.

@@ -1,122 +0,0 @@
// Extensions for Protocol Buffers to create more go like structures.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced this file with a symlink to the canonical one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok so what happens when someone checks out this repo in windows?

@awalterschulze
Copy link
Member

I will try to check out this pull request and check out which panics you are getting asap.

@awalterschulze
Copy link
Member

I found the panics, I suspect there is just something wrong your customtype implementation of Uuid.
Have you tried the test/custom/Uint128 type its an array [2]uint64
Its probably a better template to create a new custom array type from if you require it to be an array of bytes.
If you still get stuck I'll take a deeper look.

@tamird
Copy link
Contributor Author

tamird commented Feb 13, 2016

Yeah, I'm stuck. I tried using a different type, and I no longer get panics, but JSON tests are failing because the jsonpb code is unable to handle non-generated structs. Here's my branch https://github.com/tamird/gogoprotobuf/commits/customtype-array

I'd appreciate some help here :(

I also noticed that there are no tests exercising Clone in a general way (there are a few tests from upstream), but that can come later.

@awalterschulze
Copy link
Member

Yes the reason I say I don't support proto.Clone is because I did not write the tests.
Same reason I don't support proto.Equal.
Its basically just a time issue.

@awalterschulze
Copy link
Member

I am just trying to make and it breaks.

@awalterschulze
Copy link
Member

I checked out the wrong branch, my bad.

@awalterschulze
Copy link
Member

This is one way to make it work

type Array [32]byte

func (b Array) Marshal() ([]byte, error) {
    buffer := make([]byte, b.Size())
    _, err := b.MarshalTo(buffer)
    return buffer, err
}

func (u Array) MarshalTo(data []byte) (int, error) {
    return copy(data, u[:]), nil
}

func (u *Array) Unmarshal(data []byte) error {
    if copy(u[:], data) != 32 {
        return errors.New("Array: invalid length")
    }
    return nil
}

func (b Array) MarshalJSON() ([]byte, error) {
    data, err := b.Marshal()
    if err != nil {
        return nil, err
    }
    return json.Marshal(data)
}

func (u Array) Size() int {
    return 32
}

func (b *Array) UnmarshalJSON(data []byte) error {
    v := new([]byte)
    err := json.Unmarshal(data, v)
    if err != nil {
        return err
    }
    return b.Unmarshal(*v)
}

func (this Array) Equal(that Array) bool {
    return this == that
}

func NewPopulatedArray(r randy) *Array {
    array := Array{}
    for i := range array {
        array[i] = byte(r.Intn(255))
    }
    return &array
}

@awalterschulze
Copy link
Member

But it looks like there is no code to handle a struct as a customtype in the json marshal code.

@awalterschulze
Copy link
Member

Ok there is quite a bit of work to do to make this work :(
This is only the start.

@@ -312,11 +308,24 @@ func (m *Marshaler) marshalValue(out *errWriter, prop *proto.Properties, v refle
                        if prop.CustomType == "" {
                                return fmt.Errorf("%v does not implement proto.Message", v.Type())
                        }
-                       t := proto.MessageType(prop.CustomType)
-                       if t == nil || !i.Type().ConvertibleTo(t) {
-                               return fmt.Errorf("%v declared custom type %s but it is not convertible to %v", v.Type(), prop.CustomType, t)
+                       log.Printf("handling customType %v", prop.CustomType)
+                       m, ok := iface.(interface {
+                               MarshalJSON() ([]byte, error)
+                       })
+                       if !ok {
+                               log.Printf("handling customType does not implememt json.Marshal %v", prop.CustomType)
+                               return fmt.Errorf("customType %v does not implement json.Marshal", prop.CustomType)
                        }
-                       pm = i.Convert(t).Interface().(proto.Message)
+                       data, err := m.MarshalJSON()
+                       if err != nil {
+                               log.Printf("handling customType marshalJson error %v", err)
+                               return err
+                       }
+                       customStr := string(data)
+                       log.Printf("customType customStr = %v", customStr)
+                       out.write(customStr)
+                       log.Printf("handling customType out error %v", out.err)
+                       return out.err
                }
                return m.marshalObject(out, pm, indent+m.Indent)
        }
@@ -420,16 +429,19 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage) error {
        if targetType.Kind() == reflect.Struct {
                var jsonFields map[string]json.RawMessage
                if err := json.Unmarshal(inputValue, &jsonFields); err != nil {
+                       panic(err)
                        return err
                }

                sprops := proto.GetProperties(targetType)
                for i := 0; i < target.NumField(); i++ {
+
                        ft := target.Type().Field(i)
                        if strings.HasPrefix(ft.Name, "XXX_") {
                                continue
                        }
-                       fieldName := jsonProperties(ft).OrigName
+                       prop := jsonProperties(ft)
+                       fieldName := prop.OrigName

                        valueForField, ok := jsonFields[fieldName]
                        if !ok {
@@ -459,6 +471,24 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage) error {
                                f.SetInt(int64(n))
                                continue
                        }
+                       if prop.CustomType != "" {
+                               f := target.Field(i)
+                               if f.Kind() == reflect.Ptr {
+                                       f.Set(reflect.New(f.Type().Elem()))
+                               }
+                               field := f.Interface()
+                               m, ok := field.(interface {
+                                       UnmarshalJSON([]byte) error
+                               })
+                               if !ok {
+                                       return fmt.Errorf("customType %v does not implement UnmarshalJSON", prop.CustomType)
+                               }
+                               log.Printf("unmarshaling customType %v", prop.CustomType)
+                               if err := m.UnmarshalJSON(valueForField); err != nil {
+                                       return err
+                               }
+                               continue
+                       }

                        if err := unmarshalValue(target.Field(i), valueForField); err != nil {
                                return err

@awalterschulze
Copy link
Member

So this is definitely worth logging an issue.

@tamird
Copy link
Contributor Author

tamird commented Feb 15, 2016

Logged #147

@awalterschulze
Copy link
Member

Cool Thank you
So does the type Array [32]byte solution work for you in the mean time?

@tamird
Copy link
Contributor Author

tamird commented Feb 15, 2016

For now I've just removed all the problematic calls to proto.Clone(<message that contains a customtype struct>), and that seems to work. See cockroachdb/cockroach#4383.

@awalterschulze
Copy link
Member

Ok cool, then I can properly fix this when I have more time.
Btw another very hacky way to properly Clone is to Marshal and Unmarshal.

@tamird
Copy link
Contributor Author

tamird commented Feb 15, 2016

Ha, yeah, we're going to avoid that for now :)

@awalterschulze
Copy link
Member

I am guessing you found another solution. I am closing this, but please comment if I should reopen.

@tamird
Copy link
Contributor Author

tamird commented Sep 17, 2016

We wrote a wrapper that panics if you try to clone a structure that contains an array. Not ideal!

@awalterschulze
Copy link
Member

Ok well the issue #147 is still open. So we are both still waiting for a proper fix for proto.Clone

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants