Why simple XML parser doesn't fill struct - go

Trying to implement a simple XML parsing, the code below doesn't work as expected.
It just returns a {[]} empty Results, while it should fill it.
Why ?...
package main
import "fmt"
import "encoding/xml"
import "bytes"
type Name struct {
Name string `xml:"NAME"`
}
type Results struct {
Names []Name `xml:"RESULTS"`
}
func main() {
data := []byte(`
<?xml version="1.0" encoding="UTF-8"?>
<RESULTS>
<NAME>Apple</NAME>
<NAME>Banana</NAME>
</RESULTS>
`)
var r Results
decoder := xml.NewDecoder(bytes.NewBuffer(data))
unError := decoder.Decode(&r)
if unError != nil {
fmt.Println("XML Unmarshaling error:", unError )
}else{
fmt.Printf("%v", r)
}
}
Tryed in the Playground, and locally (go1.17.2).

I would suggest you to use a online struct generator like xmltogo, so use this as:
type RESULTS struct {
XMLName xml.Name `xml:"RESULTS"`
Text string `xml:",chardata"`
NAME []string `xml:"NAME"`
}
Try on playground

Related

Property name of a structure to string

I would like to know if it is possible to get the name of a property from a structure and convert it to string.
For example in the following code:
package main
import "fmt"
type StructA struct {
ValueAA string
ValueAB string
}
type StructB struct {
ValueBA string
ValueBB string
RefStructA StructA
}
func main() {
//pass any attribute of any structure
fmt.Println(castProperty(StructB.RefStructA.ValueAA))
//print the name passed but in string. Do not print the value
//expected output: "StructB.RefStructA.ValueAA"
}
func castProperty(value interface{}) string {
//some code
}
Is it possible to write a function that allows obtaining the name of the property of a structure and converted to a string? property value is not required.
That's called Reflection. I let you read the page, it lets you do what you want.
First, read the The first law of reflection: https://go.dev/blog/laws-of-reflection
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
fmt.Println("type:", reflect.TypeOf(x))
}
https://play.golang.org/p/OuGgD1TlSMO
I am not sure exactly how do you want to give input to your function but here is an example that may help you
package main
import (
"log"
"reflect"
)
func main() {
getPropertyName(B{})
}
type A struct {
field1 string
}
type B struct {
field A
}
func getPropertyName(b interface{}) {
parentType := reflect.TypeOf(b)
val := reflect.ValueOf(b)
for i := 0; i< val.Type().NumField(); i++ {
t := val.Type().Field(i)
ty := val.Type().Field(i).Type.Name()
log.Println(parentType.Name()+"."+ t.Name+"."+ty)
}
}
you could do something like this :
package main
import (
"fmt"
)
type StructA struct {
ValueAA string
ValueAB string
}
type StructB struct {
ValueBA string
ValueBB string
RefStructA StructA
}
func main() {
x := &StructB{RefStructA: StructA{ValueAA: "something"}}
fmt.Printf("%+v", x)
}
out :
&{ValueBA: ValueBB: RefStructA:{ValueAA:something ValueAB:}}

Golang unable to map XML to Struct

I want to map XML data to Struct object. I have following code:
package main
import (
"encoding/xml"
"fmt"
)
func main() {
type FileDetails struct {
XMLName xml.Name `xml:"FileDetails"`
FileName string
FileSize string
}
type DataRequest struct {
XMLName xml.Name `xml:"Data"`
DataRequestList []FileDetails
}
type Request struct {
XMLName xml.Name `xml:"Request"`
DataReqObject DataRequest `xml:"Data"`
}
req := Request{}
data := `
<Request>
<Data>
<FileDetails>
<FileName>abc</FileSize>
<FileSize>10</FileSize>
</FileDetails>
<FileDetails>
<FileName>pqr</FileSize>
<FileSize>20</FileSize>
</FileDetails>
</Data>
</Request>
`
err := xml.Unmarshal([]byte(data), &req)
if err != nil {
fmt.Printf("error: %v", err)
return
}
fmt.Printf("XMLName: %#v\n", req.XMLName)
fmt.Printf("XMLName: %v\n", req.DataReqObject)
fmt.Printf("XMLName: %v\n", req.DataReqObject.DataRequestList[0])
}
This can also be accessed here:
https://play.golang.org/p/VAMM9M2CejH
I'm getting following output with the above code:
XMLName: xml.Name{Space:"", Local:"Request"}
Data: {{ Data} []}
panic: runtime error: index out of range
Do the structs need to have diffferent structure for my data? Why is this mapping failing?
Three problems with your snippet:
#1
The tag xml:"FileDetails" is missing on DataRequestList
#2
The FileDetails struct does not match your xml in the provided playground link!
#3
<FileName> tag is closed with </FileSize> tag!
Go playground working example!

Print struct field tags while unmarshalling JSON content for a field?

In Go, Is it possible to get the tags from a struct field while I'm unmarshaling JSON content to it? Here's my failed attempt at doing so:
package main
import (
"log"
"encoding/json"
)
type Person struct {
ProfileName AltField `json:"profile_name"`
}
type AltField struct {
Val string
}
func (af *AltField) UnmarshalJSON(b []byte) error {
log.Println("Show tags")
//log.Println(af.Tag) // I want to see `json:"profile_name"`
if e := json.Unmarshal(b,&af.Val); e != nil {
return e
}
return nil
}
func main() {
p := Person{}
_ = json.Unmarshal([]byte(`{"profile_name":"Af"}`),&p)
}
I commented out the line log.Println(af.Tag) because it causes compilation errors. If I can get a handle on the tags from the Person struct, that will allow me to develop some other conditional logic.
Is this possible?
Use reflection to get the value of struct field tag. The reflect package provides functions to work with tags including to get the value of tag
package main
import (
"log"
"encoding/json"
"reflect"
)
type Person struct {
ProfileName AltField `json:"profile_name"`
}
type AltField struct {
Val string `json:"val"`
}
func (af *AltField) UnmarshalJSON(b []byte) error {
field, ok := reflect.TypeOf(*af).FieldByName("Val")
if !ok {
panic("Field not found")
}
log.Println(string(field.Tag))
if e := json.Unmarshal(b,&af.Val); e != nil {
return e
}
return nil
}
func main() {
p := Person{}
_ = json.Unmarshal([]byte(`{"profile_name":"Af"}`),&p)
}
You can only get the value of those field tags which has them. The struct field reflect object should be created for fetching the tags of its fields.
Working Code on Playground

Reading XML with golang

I'm trying to read som XML with golang. I'm basing it on this example which works. https://gist.github.com/kwmt/6135123#file-parsetvdb-go
This is my files:
Castle0.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Channel>
<Title>test</Title>
<Description>this is a test</Description>
</Channel>
test.go
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
"os"
)
type Query struct {
Chan Channel `xml:"Channel"`
}
type Channel struct {
title string `xml:"Title"`
desc string `xml:"Description"`
}
func (s Channel) String() string {
return fmt.Sprintf("%s - %d", s.title, s.desc)
}
func main() {
xmlFile, err := os.Open("Castle0.xml")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer xmlFile.Close()
b, _ := ioutil.ReadAll(xmlFile)
var q Query
xml.Unmarshal(b, &q)
fmt.Println(q.Chan)
}
Output:
- %!d(string=)
Any one know what I'm doing wrong? (I'm doing this to learn go, so go easy on me :P)
Other packages, including encoding/json and encoding/xml can only see exported data. So firstly your title and desc should be Title and Desc correspondingly.
Secondly, you're using %d (integer) format in Sprintf when printing a string. That's why you're getting %!d(string=), which means "it's not an integer, it's a string!".
Thirdly, there is no query in your XML, so unmarshal directly into q.Chan.
This is the working example. http://play.golang.org/p/l0ImL2ID-j

Missing Xml Tag in Golang

I am new to golang I am trying to make a xml in which i am using nested tags
For that my code is
type MyXml struct {
XMLName xml.Name `xml:"myXml"`
Id int `xml:"id,attr"`
NewXml
}
type NewXml struct {
XMLName xml.Name `xml:"newXml"`
OneMoreXml
}
type OneMoreXml struct {
Msg interface{} `xml:"oneMore"`
}
type Child struct {
Param1 string `xml:"Param1"`
}
func main() {
baseXml := &Child{Param1: "Param1"}
retXml := GetXml(baseXml)
fmt.Println("my xml is", retXml)
}
func MarshallXml(reqString interface{}) (newXml string) {
xmlBody, err := xml.Marshal(reqString)
if err != nil {
fmt.Printf("error: %v\n", err)
}
newXml = string(xmlBody)
//fmt.Println(newXml)
return
}
func GetXml(baseXml interface{}) (finalXml string) {
startXml := new(MyXml)
startXml.Id = 1
startXml.Msg = baseXml
finalXml = MarshallXml(startXml)
return
}
but in my output xml Tag newXml is missing. I have tried it in various ways but some tag is always missing. I guess i am not understanding struct tag properly. So What i am doing wrong in above code and which basic concept of golang struct i am missing
I had a look at the xml package doc and they say that "an anonymous struct field is handled as if the fields of its value were part of the outer struct". In your case, all fields thus get serialized as if they were part of MyXml.
NewXml does not have any field (you just give it a name but there is nothing else) so nothing gets serialized for it.
If you add a new field to it, you can see that it is serialized.
type NewXml struct {
XMLName xml.Name `xml:"newXml"`
Test int
OneMoreXml
}
http://play.golang.org/p/vibSeQHTCr

Resources