Open yangzhares opened 2 years ago
Nice find! This does indeed look like a bug with the Go-to-CUE type conversion logic. Thanks for the nice small reproducer too. Here's some slightly simpler code that reproduces the issue and also verifies that the Go type does encode correctly:
package main
import (
"encoding/json"
"fmt"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/format"
)
func main() {
ctx := cuecontext.New()
val := ctx.EncodeType(Example{})
node := val.Syntax()
raw, _ := format.Node(node)
fmt.Printf("-- gotype.cue --\n%s\n", raw)
data, _ := json.Marshal(Example{
Hello: Hello{"one"},
})
fmt.Printf("-- gotype.json --\n%s\n", data)
}
type Example struct {
Hello
}
type Hello struct {
Hello string `json:"hello"`
}
This prints the following on my machine (with CUE v0.4.3):
-- gotype.cue --
{
"": {
hello: string
}
}
-- gotype.json --
{"hello":"one"}
I ran into this today, using structs from a 3rd party package. My workaround was to create local versions of those structs, with the added json
tags.
Are there any updates on the issue? I'm working on something that could expose this to end users, which would be a potential stoppper...
After more testing I realised the 3rd party structs I was using had nested embedding on quite some levels... Hence I wrote a hacky little function to unify embedded structs with it's parent. This might be helpful for others coming here who want a hacky fix:
func cueRemoveEmbeddedAnomaly(val cue.Value) (cue.Value, error) {
iter, err := val.Fields(cue.All())
if err != nil {
return val, err
}
newVal := val.Context().CompileString("{}")
for iter.Next() {
field := iter.Value()
fieldPath := cue.MakePath(iter.Selector())
label, ok := field.Label()
if !ok {
continue
}
if field.Kind() != cue.StructKind {
newVal = newVal.FillPath(fieldPath, field)
continue
}
child, err := cueRemoveEmbeddedAnomaly(field)
if err != nil {
return val, err
}
if label == "" {
newVal = newVal.Unify(child)
continue
}
newVal = newVal.FillPath(fieldPath, child)
}
return newVal, nil
}
Now if you run your test like this, it should give the desired output:
func TestEncodeTypeWithEmbeddedHack(t *testing.T) {
type Hello struct {
Hello string `json:"hello"`
}
type Example struct {
Hello
}
ctx := cuecontext.New()
val := ctx.EncodeType(Example{})
val, err := cueRemoveEmbeddedAnomaly(val)
if err != nil {
t.Fatal(err)
}
node := val.Syntax()
raw, _ := format.Node(node)
fmt.Printf("-- gotype.cue --\n%s\n", raw)
data, _ := json.Marshal(Example{
Hello: Hello{"one"},
})
fmt.Printf("-- gotype.json --\n%s\n", data)
}
What version of CUE are you using (
cue version
)?Does this issue reproduce with the latest release?
Yes
What did you do?
When did convert a Go type to CUE value via
EncodeType
, embedded Go struct marked with a json inline tag doesn't unify with struct definition, an empty CUE field will map to CUE value of embedded Go struct.Reproduce this via below code:
What did you expect to see?
What did you see instead?