Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I really don't understand how to parse this response from the Api using Go since I see XML first and then Json:
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://www.zzap.ru/">{"error":"","class_man":"MITSUBISHI","logopath":"https://koj.blob.core.windows.net/zzap-upload/upload/logos/se12d7724469c1dbbe07e303ac6e91b48.png","partnumber":"MR245368","class_cat":"windscreen washer motor","imagepath":"","code_cat":1116901944,"class_cur":"р.","price_count_instock":24,"price_min_instock":200.0,"price_avg_instock":810.0,"price_max_instock":1380.0,"price_count_order":457,"price_min_order":201.0,"price_avg_order":1079.0,"price_max_order":8004.0,"imagepathV2":[""],"code_man":3113}</string>
The following code will unmarshal the xml first and then unmarshal the json in the Text field of the struct the xml was unmarshalled to. Below is a link to the playground where you can run the example and play with it.
package main
import (
"encoding/json"
"encoding/xml"
"fmt"
"log"
)
type xmlStructure struct {
XMLName xml.Name `xml:"string"`
Text string `xml:",chardata"`
XMLNS string `xml:"xmlns,attr"`
}
type jsonStructure struct {
Error string `json:"error"`
ClassMan string `json:"class_man"`
LogoPath string `json:"logo_path"`
PartNumber string `json:"part_number"`
ClassCat string `json:"class_cat"`
// etc.
}
func main() {
var input = `<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://www.zzap.ru/">{"error":"","class_man":"MITSUBISHI","logopath":"https://koj.blob.core.windows.net/zzap-upload/upload/logos/se12d7724469c1dbbe07e303ac6e91b48.png","partnumber":"MR245368","class_cat":"windscreen washer motor","imagepath":"","code_cat":1116901944,"class_cur":"р.","price_count_instock":24,"price_min_instock":200.0,"price_avg_instock":810.0,"price_max_instock":1380.0,"price_count_order":457,"price_min_order":201.0,"price_avg_order":1079.0,"price_max_order":8004.0,"imagepathV2":[""],"code_man":3113}</string>`
var in xmlStructure
if err := xml.Unmarshal([]byte(input), &in); err != nil {
log.Fatal(err)
}
var msg jsonStructure
if err := json.Unmarshal([]byte(in.Text), &msg); err != nil {
log.Fatal(err)
}
fmt.Printf("%+v", msg)
}
Go playground
Note: The jsonStructure type is still missing some fields which where in your example.
Note2: Since you are new to Go:
The key to the problem is the xml and json tags behind the struct fields. They must match the field names of the xml/json input you get. For xml there are some special cases like the xml.Name type that is needed to match a struct to an xml tag. (By this the entire xmlStructure struct is matched to the string tag from the input.) Also the xml:"xmlns,attr" behind the XMLNS field tells the xml package to look for an attribute called xmlns in the string tag. The missing name in xml:",chardata" tells xml to select the content of the string tag.
The json tags are simpler, only stating the matching name in the input.
Also note, that the fields themselves of the structs must be exported, otherwise they are not accessible to the xml/json packages and cannot be filled.
Related
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
I need to parse a JSON into Go struct. Following is the struct
type Replacement struct {
Find string `json:"find"`
ReplaceWith string `json:"replaceWith"`
}
Following is an example json:
{
"find":"TestValue",
"replaceWith":""
}
The input json can have empty values for some field. Go's encoding/json library by default takes nil value for any empty string provided in JSON.
I've a downstream service, which finds and replaces the replaceWith values in configurations. This is causing issues with my downstream service as it doesn't accept nil for the replaceWith parameter. I have a workaround where I'm replacing nil values by "''" but this can cause an issue where some value is replaced with ''. Is there a way for json to not parse empty string as nil and just ""
Here is a link to the code: https://play.golang.org/p/SprPz7mnWR6
In Go string type cannot hold nil value which is zero value for pointers, interfaces, maps, slices, channels and function types, representing an uninitialized value.
When unmarshalling JSON data to struct as you do in your example ReplaceWith field will indeed be an empty string ("") - which is exactly what you are asking for.
type Replacement struct {
Find string `json:"find"`
ReplaceWith string `json:"replaceWith"`
}
func main() {
data := []byte(`
{
"find":"TestValue",
"replaceWith":""
}`)
var marshaledData Replacement
err := json.Unmarshal(data, &marshaledData)
if err != nil {
fmt.Println(err)
}
if marshaledData.ReplaceWith == "" {
fmt.Println("ReplaceWith equals to an empty string")
}
}
You can use Pointer in string and if the value is missing in JSON then it would be nil. I have done the same in past but currently I don't have code with me.
This question already has answers here:
Lowercase JSON key names with JSON Marshal in Go
(4 answers)
Closed 5 years ago.
What I am trying to do
I am parsing a JSON HTTP response based on this answer to a similar question. My code is able to parse the JSON without any error but is unable to read the values and store them in the provided variable.
This has been puzzling me for the last 2 hours and it might be due to a trivial reason that I am overlooking here.
CODE
type ImporterResponse struct {
results []packagemeta `json:"results"`
}
type packagemeta struct {
path string `json:"path"`
synopsis string `json:"synopsis,omitempty"`
count int `json:"import_count,omitempty`
}
func main() {
res := []byte(`{"results":[{"path":"4d63.com/randstr/lib/randstr","import_count":0,"synopsis":"Package randstr generates random strings (e.g."},{"path":"bitbucket.org/pcas/tool/mathutil","import_count":0}]}`)
fmt.Println("Decoding the JSON")
r := bytes.NewReader(res)
decoder := json.NewDecoder(r)
packageimporters := &ImporterResponse{}
err := decoder.Decode(packageimporters)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Packageimporters: %+v", packageimporters)
fmt.Println(len(packageimporters.results))
}
Link to Playground: https://play.golang.org/p/NzLl7Ujo2IJ
What I want:
How to fix this?
Why is no error message raised if JSON is not parsed properly?
P.S: I understand that this question has been asked before and there are possible solutions available but none of them work for me. Hence, I have made this post.
You need to make your struct fields exported, otherwise the json package cannot access them.
Please read JSON and go for more details, specifically this paragraph:
The json package only accesses the exported fields of struct types
(those that begin with an uppercase letter). Therefore only the the
exported fields of a struct will be present in the JSON output.
And this one for more details:
How does Unmarshal identify the fields in which to store the decoded
data? For a given JSON key "Foo", Unmarshal will look through the
destination struct's fields to find (in order of preference):
An exported field with a tag of "Foo" (see the Go spec for more on
struct tags),
An exported field named "Foo", or
An exported field
named "FOO" or "FoO" or some other case-insensitive match of "Foo".
So your struct should really be:
type Packagemeta struct {
Path string `json:"path"`
Synopsis string `json:"synopsis,omitempty"`
Count int `json:"import_count,omitempty`
}
This question already has an answer here:
json.Unmarshal returning blank structure
(1 answer)
Closed 3 years ago.
I am new to Go, and I am trying to practice with building a simple HTTP server. However I met some problems with JSON responses. I wrote following code, then try postman to send some JSON data. However, my postman always gets an empty response and the content-type is text/plain; charset=utf-8. Then I checked a sample in http://www.alexedwards.net/blog/golang-response-snippets#json. I copied and pasted the sample, and it was working well. But I cannot see any difference between mine and the sample. Can someone give some help?
package main
import (
"encoding/json"
"net/http"
)
type ResponseCommands struct {
key string
value bool
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":5432", nil)
}
func handler(rw http.ResponseWriter, req *http.Request) {
responseBody := ResponseCommands{"BackOff", false}
data, err := json.Marshal(responseBody)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
rw.WriteHeader(200)
rw.Header().Set("Content-Type", "application/json")
rw.Write(data)
}
The main difference is that the variable in the struct are public (exported)
type Profile struct {
Name string
Hobbies []string
}
In your case, they are not (lowercase).
type ResponseCommands struct {
key string
value bool
}
See "Lowercase JSON key names with JSON Marshal in Go".
As VonC already answered correct. Just want to add that IDEA can help with such 'small' problems.
I'm using Gogland and it let me know that json tag cannot be applied to lowercase field.
I have a struct VideoInfo that has a key in it called embedCode. The API I am querying returns the embed code as embed_code. During unmarshalling the response how do I ensure embed_code goes into embedCode?
Also is there an easy way to take a large json string and automatically turn it into a struct, or can one only use a map?
With respect to remapping the field names use the corresponding annotation in the structure declaration:
type VideoInfo struct {
EmbedCode string `json:"embed_code"`
}
The marshaller/un-marshaller will only process public field, so you need to capitalise the field name.
With respect to converting the whole structure, yes it is easy. Declare an instance to un-marshal into and pass a reference to the json.Unmarshal method (from a test):
data, _ := json.Marshal(request)
var resp response.VideoInfo
if err := json.Unmarshal(data, &resp); err != nil {
t.Errorf("unexpected error, %v", err)
}
At first, struct's field must be start from capital letter to be public. So you need something like that:
type VideoInfo struct {
EmbedCode string `json:"embed_code"`
}
And look at documentation for more info.
This question already has answers here:
JSON and dealing with unexported fields
(2 answers)
(un)marshalling json golang not working
(3 answers)
json.Marshal(struct) returns "{}"
(3 answers)
Printing Empty Json as a result [duplicate]
(1 answer)
Parsing JSON in Golang doesn't Populate Object [duplicate]
(1 answer)
Closed 10 months ago.
I'm a new Go programmer (From Java) and I would like to reproduce a generic way which is esay to use in Java.
I want to create some function which allow me to do an Unmarshal on a JSON string in order to avoid code duplicity.
This is my current code which is not working :
type myStruct1 struct {
id string
name string
}
func (obj myStruct1) toString() string {
var result bytes.Buffer
result.WriteString("id : ")
result.WriteString(obj.id)
result.WriteString("\n")
result.WriteString("name : ")
result.WriteString(obj.name)
return result.String()
}
func main() {
content := `{id:"id1",name="myName"}`
object := myStruct1{}
parseJSON(content, object)
fmt.Println(object.toString())
}
func parseJSON(content string, object interface{}) {
var parsed interface{}
json.Unmarshal([]byte(content), &parsed)
}
This code, on run, returns me this :
id :
name :
Do you have any idea ?
Thanks
The issue is you want to write to a generic type? You probably want a string map. This works with BSON anyways:
var anyJson map[string]interface{}
json.Unmarshal(bytes, &anyJson)
You'll be able to access the fields like so:
anyJson["id"].(string)
Don't forget to type assert your values, and they must be the correct type or they'll panic. (You can read more about type assertions on the golang site)
To parse "generic JSON" when you have no idea what schema it has:
var parsed any
err := json.Unmarshal(jsonText, &parsed)
The returned any in parsed will be a map[string]any or []any or nil or single values float64, bool, string.
You can test the type and react accordingly.
import (
"encoding/json"
"fmt"
)
func test(jsonText []byte) {
// parsing
var parsed any
err := json.Unmarshal(jsonText, &parsed)
if err != nil {
panic(err) // malformed input
}
// type-specific logic
switch val := parsed.(type) {
case nil:
fmt.Println("json specifies null")
case map[string]any:
fmt.Printf("id:%s name:%s\n", val["id"], val["name"])
case []any:
fmt.Printf("list of %d items\n", len(val))
case float64:
fmt.Printf("single number %f\n", val)
case bool:
fmt.Printf("single bool %v\n", val)
case string:
fmt.Printf("single string %s\n", val)
default:
panic(fmt.Errorf("type %T unexpected", parsed))
}
}
Unmarshal will only set exported fields of the struct.
Which means you need to modify the json struct to use capital case letters:
type myStruct1 struct {
Id string
Name string
}
The reason behind this is that the json library does not have the ability to view fields using reflect unless they are exported.
You have to export your fields:
type myStruct1 struct {
Id string
Name string
}
See Exported Identifiers from documentation.
There are a few changes you need to make in your code to make it work:
The function json.Unmarshal can only set variables inside your struct which are exported, that is, which start with capital letters. Use something like ID and Name for your variable names inside myStruct1.
Your content is invalid JSON. What you actually want is {"ID":"id1","Name":"myName"}.
You're passing object to parseJSON but you're using parsed instead, not object. Make parseJSON receive a *myStruct (instead of an interface{}), and use that variable instead of parsed when unmarshalling the string. Also, always handle the error returns, like err := json.Unmarshal(content, object), and check err.
I'd suggest you to do the Golang Tour ;)
You can also set the file as an Object with dynamic properties inside another struct. This will let you add metadata and you read it the same way.
type MyFile struct {
Version string
Data map[string]interface{}
}