robertkrimen / otto

A JavaScript interpreter in Go (golang)
http://godoc.org/github.com/robertkrimen/otto
MIT License
8.04k stars 584 forks source link

[BUG] Otto fail marshal to json a array of string to object #382

Closed amreo closed 1 year ago

amreo commented 4 years ago

Otto version: c382bd3c16ff2fef9b5fe0dd8bf4c4ec6bfe62c1

Actual: Otto marshal a javascript array of string to a JSON object Expected: I expected that Otto marshal a javascript array to a JSON array

Output:

foo,bar
object
foo,bar
object
{"Bar":["foo","bar"],"Foo":{}}
panic: json: cannot unmarshal object into Go struct field Foobar.Foo of type []string

goroutine 1 [running]:
main.main()
        /tmp/pippo/main.go:59 +0x4a8
exit status 2

Example code:

package main

import (
    "encoding/json"
    "fmt"

    "github.com/robertkrimen/otto"
)

func main() {
    var err error
    type Foobar struct {
        Foo []string    
        Bar []string    

    }

    var value Foobar
    var temp map[string]interface{}

    //Decode value to temp
    tempRaw, err := json.Marshal(value)
    if err != nil {
        panic(err)
    }
    err = json.Unmarshal(tempRaw, &temp)
    if err != nil {
        panic(err)
    }

    //run otto
    vm := otto.New()
    err = vm.Set("foobar", temp) //using &value instead of temp work in this example but not in my exact usecase 
    if err != nil {
        panic(err)
    }
    err = vm.Set("bar", []string{"foo", "bar"})
    if err != nil {
        panic(err)
    }
    _, err = vm.Run(`
        console.log([ 'foo', 'bar' ]);
        console.log(typeof [ 'foo', 'bar' ]);
        console.log(bar);
        console.log(typeof bar);
        foobar.Foo = [ 'foo', 'bar' ]; //don't work
        foobar.Bar = bar; //work
    `)
    if err != nil {
        panic(err)
    }

    //Encode temp to value
    tempRaw, err = json.Marshal(temp)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(tempRaw))
    err = json.Unmarshal(tempRaw, &value)
    if err != nil {
        panic(err)
    }

    fmt.Println(value)
}
stevenh commented 1 year ago

This appears to work as expected now using:

func Test_issue382(t *testing.T) {
    var err error
    type Foobar struct {
        Foo []string
        Bar []string
    }

    var value Foobar
    var temp map[string]interface{}

    // Decode value to temp.
    tempRaw, err := json.Marshal(value)
    require.NoError(t, err)
    err = json.Unmarshal(tempRaw, &temp)
    require.NoError(t, err)

    vm := New()
    err = vm.Set("foobar", temp) // Using &value instead of temp work in this example but not in my exact use case.
    require.NoError(t, err)

    err = vm.Set("bar", []string{"foo", "bar"})
    require.NoError(t, err)

    _, err = vm.Run(`
        console.log([ 'foo', 'bar' ]);
        console.log(typeof [ 'foo', 'bar' ]);
        console.log(bar);
        console.log(typeof bar);
        foobar.Foo = [ 'foo', 'bar' ]; //don't work
        foobar.Bar = bar; //work
    `)
    require.NoError(t, err)

    // Encode temp to value.
    tempRaw, err = json.Marshal(temp)
    require.NoError(t, err)

    fmt.Println(string(tempRaw))
    err = json.Unmarshal(tempRaw, &value)
    require.NoError(t, err)

    fmt.Println(value)
}