I have a struct that includes an array of another struct, eg
type Struct1 struct {
Value string
Items []Struct2
}
type Struct2 struct {
Value string
}
I am using gorilla schema to decode my Form values into Struct 1.
The values for the embedded struct, Struct 2, are not coming through.
When I look at the logs for the FormValue("Struct2") it returns '[Object object], [Object object]'
Any help would be greatly appreciated
EDIT
An example of the structure of the form,
Using AngularJS,
var data = $scope.struct1;
$http({
method: 'POST',
url:url,
data : data,
headers : {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
for(var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
}
})
.then(function successCallback(response) {
console.log(response);
}, function errorCallback(response) {
});
It's possible that you don't have the right struct design for your HTML form. You might want to post your input and/or your HTML form design.
gorilla/schema's decode expects the values input to be passed as a variable of type map[string][]string, as you can see both from the example in the documentation and from the test files in the package. Here's a simple complete script that just wraps the example from the documentation and prints the result:
package main
import(
"fmt"
"github.com/gorilla/schema"
)
type Person struct {
Name string
Phone string
}
func main() {
values := map[string][]string{
"Name": {"John"},
"Phone": {"999-999-999"},
}
person := new(Person)
decoder := schema.NewDecoder()
decoder.Decode(person, values)
fmt.Printf("Person: %v\n", person)
}
That outputs Person: &{John 999-999-999}.
That is the correct form for a map literal of type map[string][]string, as you can demonstrate by performing a declaration followed by an assignment and running it without error:
var values map[string][]string
values = map[string][]string{
"Name": {"John"},
"Phone": {"999-999-999"},
}
Now map[string][]string doesn't obviously support all the types that gorilla/schema supports, such as the type in your question: slices of structs. But the HTML form is processed such that a translation makes sense: it keeps appending indices and field names with dot separators to create the desired structure. So for the types you posted in your question, I wrote this script to decode values into the structs:
package main
import(
"fmt"
"github.com/gorilla/schema"
)
type Struct1 struct {
Value string
Items []Struct2
}
type Struct2 struct {
Value string
}
func main() {
values := map[string][]string{
"Value": {"the thing with the items"},
"Items.0.Value": {"a"},
"Items.1.Value": {"b"},
"Items.2.Value": {"c"},
}
s1 := new(Struct1)
decoder := schema.NewDecoder()
decoder.Decode(s1, values)
fmt.Printf("S1: %v\n", s1)
}
Running that outputs:
S1: &{the thing with the items [{a} {b} {c}]}
That demonstrates that decode can populate your struct design without error, if its input matches that design.
So you might try to verify that your input matches that scheme -- that it has those array-like indices and field names with the dot separator in a way that conforms to your struct design. And if it does not, that indicates that your struct design needs to be updated to fit the format of your input.
You can see examples of decode working on this type of structure in the decode_test.go file in the gorilla/schema package, such as these lines:
type Foo struct {
F01 int
F02 Bar
Bif []Baz
}
type Bar struct {
F01 string
F02 string
F03 string
F14 string
S05 string
Str string
}
type Baz struct {
F99 []string
}
func TestSimpleExample(t *testing.T) {
data := map[string][]string{
"F01": {"1"},
"F02.F01": {"S1"},
"F02.F02": {"S2"},
"F02.F03": {"S3"},
"F02.F14": {"S4"},
"F02.S05": {"S5"},
"F02.Str": {"Str"},
"Bif.0.F99": {"A", "B", "C"},
}
The Foo struct has a field named Bif of type []Baz. Baz is a struct -- so we have a slice of structs type, like in your question. Baz has a field named F99. You can see that the input is referenced with the string value "Bif.0.F99".
Use this and other examples in the test file as your guide.
Related
Please help to call the variable from another variable.
I have the script which is taking EC2 instances and return in "*ec2.Instance" variable.
I can print information from static text, for example :
fmt.Println(instance.InstanceType) // t3.small
But I have the list of reqired fields like and don't know how dynamic use name from this list :
fields := []string{"InstanceId", "InstanceType", "PrivateIpAddress"}
for i := range fields {
fmt.Println(fields[i])
fmt.Println(instance.fields[i]) // Not correct ... :(
}
You need to use reflection to do this in go.
The key takeaway is you need to "analyze" the returned value at runtime and access properties by name from the "reflected" structure. Reflection basically means analyzing objects at runtime.
package main
import (
"fmt"
"reflect"
)
type whatever struct {
cat string
dog string
animals int
something string
}
func main() {
wantProps := []string{ "cat", "animals"}
we := whatever{cat: "meow", animals: 22}
r := reflect.ValueOf(we)
for _, propName := range wantProps {
prop := r.FieldByName(propName)
fmt.Println(propName, prop)
}
}
More details:
Golang dynamic access to a struct property
I have not found a way to allocate values defined in an array within a YAML file field to the fields of a struct in Go. I am already unmarshalling the whole file to a defined struct, but I would like to go deeper.
The YAML file is a hardcoded file.
YAML File
- id : [apples,bananas]
fruits: true
vegetables: false
- id : [apples,onions]
fruits: true
vegetables: true
Go structs
type Basket struct {
ID RawID `yaml:"id"`
Content BasketContent
}
type RawID struct {
Apples bool `yaml:"apples"`
Bananas bool `yaml:"bananas"`
Onions bool `yaml:"onions"`
}
type BasketContent struct {
Fruits boolean `yaml:"fruits"`
Vegetables boolean `yaml:"vegetables"`
}
I am expecting to identify which elements are located in the id field, to later hash them into a value.
You could try the following package that has definitions of yaml to structs
https://github.com/go-yaml/yaml
This includes documentation on how to handle arrays.
Edit 1: Including relevant code snippet
var data = `
a: Easy!
b:
c: 2
d: [3, 4]
`
// Note: struct fields must be public in order for unmarshal to
// correctly populate the data.
type T struct {
A string
B struct {
RenamedC int `yaml:"c"`
D []int `yaml:",flow"`
}
}
Playground
I'm trying to store a string into a slice field inside a struct. This is for collecting data and create a Json to post via to an API.
package main
type response1 struct {
Identifier string `json:"identifier"`
Family string `json:"family"`
Values struct {
Logo []struct {
Data string `json:"data"`
Scope string `json:"scope"`
} `json:"logo"`
}
}
func main() {
res2D := &response1{
Identifier: "1234567",
Family: "example",
}
res2D.Values.Logo[0].Data = "test"
res2B, _ := json.Marshal(res2D)
fmt.Println(string(res2B))
}
Error
And the error I got:
panic: runtime error: index out of range
goroutine 1 [running]:
main.main()
/tmp/sandbox507443306/main.go:22 +0xe0
You do not have to make the slice with appropriate size before hand. You can use append. What you are trying to do in your example is assign a slice "[0]" that has not been created yet, which is why you are getting your error. Use append and change your line
res2D.Values.Logo[0].Data = "test"
to
res2D.Values.Logo = append(res2D.Values.Logo,struct {Data string "json:\"data\"" }{Data: "test"})
and that will append the literal struct into your array. Now by looking at your code I am assuming you doing this as a test to explore the language so I wont go into detail on how to better write this without knowing what you are actually using it for.
I want to validate byte array data if it contains valid JSON using unmarsall method into interface.
package main
import (
"encoding/json"
"fmt"
)
func isJSON(s string) bool {
var js map[string]interface{}
return json.Unmarshal([]byte(s), &js) == nil
}
func main() {
var tests = []string{
`{"a":"b"}`,
`[{"a":"b"},{"a":"b"}]`,
}
for _, t := range tests {
fmt.Printf("isJSON(%s) = %v\n\n", t, isJSON(t))
}
}
Both input test parameters are valid JSON strings, but it validate based on the interface 'map[string]interface{}'
{
"a": "b"
}
[{
"a": "b"
}, {
"a": "b"
}]
I want to validate the JSON text. JSON text is a serialized object or array. Hence looking for a solution which support all valid cases for JSON text as I added test cases in playground.
How can I make this interface i.e var map[string]interface{} generic so it support both test cases of valid JSON string?
Solved: Playground Link
Don't use map[string]interface{} but simply interface{}. The second example is of type []interface{} and there are more types of valid json.
Here your working code. I added a few more cases of valid json.
playground
Here the code if you want to allow only maps and slices:
playground
I'm working on s small server-client project based on JSON communication. But i run into issues. I'm trying to create a response struct with a generic message body. This means I have an map with a key as string and a json raw message as value. In the end the message body should work for any type (strings, integers, arrays)
package main
import (
"encoding/json"
"fmt"
)
type ServerResponse struct {
Code int `json:"code" bson:"code"`
Type string `json:"type" bson:"type"`
Body map[string]json.RawMessage `json:"body" bson:"body"`
}
func NewServerResponse() *ServerResponse {
return &ServerResponse{Body: make(map[string]json.RawMessage)}
}
func main(){
serverResponse := NewServerResponse()
serverResponse.Code = 100
serverResponse.Type = "molly"
serverResponse.Body["string"] = json.RawMessage("getIt")
serverResponse.Body["integer"] = json.RawMessage{200}
serverResponse.Body["array"] = json.RawMessage(`["a", "b", "c"]`)
if d, err := json.Marshal(&serverResponse); err != nil{
fmt.Println("Error " + err.Error())
}else{
fmt.Println(string(d))
}
}
But the output is as follow.
{
"code":100,
"type":"molly",
"body": {
"array":"WyJhIiwgImIiLCAiYyJd",
"integer":"yA==",
"string":"Z2V0SXQ="
}
}
It seems like the values are Base64 encoded and inside double quotes. Tihs should be the expected output
{
"code":100,
"type":"molly",
"body": {
"array":["a", "b", "c"],
"integer":200,
"string":"getIt"
}
}
Is this even possible? Or do I have to write a specific struct type for every response?
The raw message must be valid JSON.
Add quotes to the string to make it a valid JSON string.
serverResponse.Body["string"] = json.RawMessage("\"getIt\"")
JSON numbers are a sequence of decimal bytes. A number is not the value of a single byte as attempted in the question.
serverResponse.Body["integer"] = json.RawMessage("200")
This one works as you expected.
serverResponse.Body["array"] = json.RawMessage(`["a", "b", "c"]`)
The program in the question compiles and runs with errors. Examining those errors and fixing them leads to my suggestions above.
An alternative approach is to replace the use of json.RawMessage with interface{}:
type ServerResponse struct {
Code int `json:"code" bson:"code"`
Type string `json:"type" bson:"type"`
Body map[string]interface{} `json:"body" bson:"body"`
}
Set the response body like this:
serverResponse.Body["string"] = "getIt"
serverResponse.Body["integer"] = 200
serverResponse.Body["array"] = []string{"a", "b", "c"}
You can use json.RawMessage values:
serverResponse.Body["array"] = json.RawMessage(`["a", "b", "c"]`)
Playground example