i would like to parse a xml file and print values.
<alerts time="2017-09-14T15:46:00+02:00">
<alert type="A" identifier="123" declaredBy="Bob" />
<startedAt>20171010212210</startedAt>
<lastUpdate bySource="X">
<updatedAt>20171010213655</updatedAt>
<eventConfirmation>unconfirmed</eventConfirmation>
<additional>
<data name="numberOfOccurences">1</data>
</additional>
</lastUpdate>
<labels>
<label language="FR">attaque DNS</Label>
</labels>
</alert>
</alerts>
in this example i have just one alert but i can have more. I wrote 2 packages
package alerts
import "encoding/xml"
// Alert structure
type Alerts struct {
XMLName xml.Name `xml:"alerts"`
Time string `xml:"time,attr"`
Alert []alert `xml:"alert"`
}
type lastUpdate struct {
XMLName xml.Name `xml:"lastUpdate"`
BySource string `xml:"bySource,attr"`
UpdatedAt string `xml:"updatedAt"`
EventConfirmation string `xml:"eventConfirmation"`
Additional additional `xml:"additional"`
}
type additional struct {
XMLName xml.Name `xml:"additional"`
Data data `xml:"data"`
}
type data struct {
XMLName xml.Name `xml:"data"`
NumberOfOccurences int `xml:"numberOfOccurences,attr"`
}
type labels struct {
XMLName xml.Name `xml:"labels"`
Label []label `xml:"label"`
}
type label struct {
XMLName xml.Name `xml:"label"`
Label string `xml:"label"`
LabelLanguage string `xml:"language,attr"`
}
type alert struct {
XMLName xml.Name `xml:"alert"`
Type string `xml:"type,attr"`
Identifier string `xml:"identifier,attr"`
DeclaredBy string `xml:"declaredBy,attr"`
StartedAt string `xml:"startedAt"`
Lastupdate lastUpdate `xml:"lastupdate"`
Labels labels `xml:"label"`
}
my second package
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
"os"
"github.com/user/xmlparser/alert"
"github.com/golang/glog"
)
func main() {
//open XML file given in argument
xmlfile, err := os.Open(os.Args[1])
if err != nil {
glog.Fatalf("opening file %s - error : %s", os.Args[1], err)
}
defer xmlfile.Close()
// read our opened xmlFile as a byte array.
byteValue, _ := ioutil.ReadAll(xmlfile)
// we initialize our alerts array
var al alerts.Alerts
// we unmarshal our byteArray which contains our
// xmlFiles content into 'al' which we defined above
xml.Unmarshal(byteValue, &al)
for i := 0; i < len(al.Alert); i++ {
fmt.Printf("Alert time : %s\n\n", al.Time)
fmt.Println("Type: " + al.Alert[i].Type)
fmt.Println("Identifier: " + al.Alert[i].Identifier)
fmt.Println("declaredBy: " + al.Alert[i].DeclaredBy)
fmt.Printf("startAt: %s\n", al.Alert[i].StartedAt)
}
}
I can print Time, Type, Identifier and DeclaredBy but the variable StartedAt is empty. There is something that i don't understand. I think that my structure is not correctly defined.
If someone can help me ... Thanks !!!
It looks like you need to define StartedAt as more than just a string. Take a look at this question and example. You need to define StartedAt as it's own struct so you can access it's innerxml string.
type StartedAt struct {
Raw string `xml:",innerxml"`
}
type alert struct {
XMLName xml.Name `xml:"alert"`
Type string `xml:"type,attr"`
Identifier string `xml:"identifier,attr"`
DeclaredBy string `xml:"declaredBy,attr"`
StartedAt StartedAt `xml:"startedAt"`
}
Related
I'm relatively new to the go language.
I have a problem trying to unmarshal a SOAP message. My attempt is to abstract the content of the Body element and avoid defining the XML structure statically, as it changes depending on the requested action.
Unfortunately I can't find a way to do this correctly. In the example, the GetContent function should receive a pointer to the struct that contains the content and add it dynamically to the Body, in order to be filled. But the result is not the expected one.
package main
import (
"encoding/xml"
"fmt"
)
type Message interface{}
type EnvelopeResponse struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
Body Message `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}
type Body struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
Fault *Fault `xml:",omitempty"`
Content Message `xml:",omitempty"`
SOAPBodyContentType string `xml:"-"`
}
type Fault struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`
Code string `xml:"faultcode,omitempty"`
String string `xml:"faultstring,omitempty"`
Actor string `xml:"faultactor,omitempty"`
Detail string `xml:"detail,omitempty"`
}
type GetHostNumberOfEntriesResponse struct {
XMLName xml.Name `xml:"urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse"`
NewHostNumberOfEntries int64 `xml:"NewHostNumberOfEntries"`
}
func GetContent(rawXml []byte, content interface{}) {
envelope := EnvelopeResponse{Body: Body{Content: content}}
xml.Unmarshal(rawXml, &envelope)
}
func main() {
b := []byte(`
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:GetHostNumberOfEntriesResponse xmlns:u="urn:dslforum-org:service:Hosts:1">
<NewHostNumberOfEntries>47</NewHostNumberOfEntries>
</u:GetHostNumberOfEntriesResponse>
</s:Body>
</s:Envelope>
`)
content := &GetHostNumberOfEntriesResponse{}
GetContent(b, content)
fmt.Println(*content)
}
Here the example in the playground:
https://go.dev/play/p/BBR4vEXiPbc
you use some interface{}, Why? Follow is test pass code:
https://go.dev/play/p/OPkNScHjri1
package main
import (
"encoding/xml"
"fmt"
)
type Message interface{}
type EnvelopeResponse struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
Body Body
}
type Body struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
GetHostNumberOfEntriesResponse GetHostNumberOfEntriesResponse
Fault *Fault `xml:",omitempty"`
Content Message `xml:",omitempty"`
SOAPBodyContentType string `xml:"-"`
}
type Fault struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`
Code string `xml:"faultcode,omitempty"`
String string `xml:"faultstring,omitempty"`
Actor string `xml:"faultactor,omitempty"`
Detail string `xml:"detail,omitempty"`
}
type GetHostNumberOfEntriesResponse struct {
XMLName xml.Name `xml:"urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse"`
NewHostNumberOfEntries int64 `xml:"NewHostNumberOfEntries"`
}
func GetContent(rawXml []byte) *EnvelopeResponse {
envelope := EnvelopeResponse{}
xml.Unmarshal(rawXml, &envelope)
return &envelope
}
func main() {
b := []byte(`
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:GetHostNumberOfEntriesResponse xmlns:u="urn:dslforum-org:service:Hosts:1">
<NewHostNumberOfEntries>47</NewHostNumberOfEntries>
</u:GetHostNumberOfEntriesResponse>
</s:Body>
</s:Envelope>
`)
envelope := GetContent(b)
fmt.Println(envelope)
}
&{{http://schemas.xmlsoap.org/soap/envelope/ Envelope} {{http://schemas.xmlsoap.org/soap/envel
ope/ Body} {{urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse} 47} <nil> <nil>
}}
The solution I found is to use generics to represent the variable and parameterized content of the body.
This code works as I expect:
package main
import (
"encoding/xml"
"fmt"
)
type EnvelopeResponse[T any] struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
Body Body[T] `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}
type Body[T any] struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
Fault *Fault `xml:",omitempty"`
Content T `xml:",omitempty"`
SOAPBodyContentType string `xml:"-"`
}
type Fault struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`
Code string `xml:"faultcode,omitempty"`
String string `xml:"faultstring,omitempty"`
Actor string `xml:"faultactor,omitempty"`
Detail string `xml:"detail,omitempty"`
}
type GetHostNumberOfEntriesResponse struct {
XMLName xml.Name `xml:"urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse"`
NewHostNumberOfEntries int64 `xml:"NewHostNumberOfEntries"`
}
func GetContent[T any](rawXml []byte, content T) {
envelope := EnvelopeResponse[T]{Body: Body[T]{Content: content}}
xml.Unmarshal(rawXml, &envelope)
}
func main() {
b := []byte(`
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:GetHostNumberOfEntriesResponse xmlns:u="urn:dslforum-org:service:Hosts:1">
<NewHostNumberOfEntries>47</NewHostNumberOfEntries>
</u:GetHostNumberOfEntriesResponse>
</s:Body>
</s:Envelope>
`)
content := &GetHostNumberOfEntriesResponse{}
GetContent(b, content)
fmt.Println(*content)
}
If I have a struct which I want to be able to Marhsal/Unmarshal things in and out of xml with (using encoding/xml) - how can I not print attributes which are empty?
package main
import (
"encoding/xml"
"fmt"
)
type MyThing struct {
XMLName xml.Name `xml:"body"`
Name string `xml:"name,attr"`
Street string `xml:"street,attr"`
}
func main() {
var thing *MyThing = &MyThing{Name: "Canister"}
result, _ := xml.Marshal(thing)
fmt.Println(string(result))
}
For example see http://play.golang.org/p/K9zFsuL1Cw
In the above playground I'd not want to write out my empty street attribute; how could I do that?
Use omitempty flag on street field.
From Go XML package:
a field with a tag including the "omitempty" option is omitted
if the field value is empty. The empty values are false, 0, any
nil pointer or interface value, and any array, slice, map, or
string of length zero.
In case of your example:
package main
import (
"encoding/xml"
"fmt"
)
type MyThing struct {
XMLName xml.Name `xml:"body"`
Name string `xml:"name,attr"`
Street string `xml:"street,attr,omitempty"`
}
func main() {
var thing *MyThing = &MyThing{Name: "Canister"}
result, _ := xml.Marshal(thing)
fmt.Println(string(result))
}
Playground
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
Take a look at this snip found at here
import (
"encoding/xml"
"fmt"
"os"
)
func main() {
type Address struct {
City, State string
}
type Person struct {
XMLName xml.Name `xml:"person"`
Id int `xml:"id,attr"`
FirstName string `xml:"name>first"`
LastName string `xml:"name>last"`
Age int `xml:"age"`
Height float32 `xml:"height,omitempty"`
Married bool
Address
Comment string `xml:",comment"`
}
v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}
v.Comment = " Need more details. "
v.Address = Address{"Hanga Roa", "Easter Island"}
enc := xml.NewEncoder(os.Stdout)
enc.Indent(" ", " ")
if err := enc.Encode(v); err != nil {
fmt.Printf("error: %v\n", err)
}
}
I can understand in the struct Person, It has a var called Id, which is of type int, but what about the stuff xml:"person" after int? What does it mean? Thanks.
It's a struct tag. Libraries use these to annotate struct fields with extra information; in this case, the module encoding/xml uses these struct tags to denote which tags correspond to the struct fields.
which mean that variable will present in the name of Person example
type sample struct {
dateofbirth string `xml:"dob"`
}
In the above example, the field 'dateofbirth' will present in the name of 'dob' in the XML.
you will see this notation often in go struct.
I have been using unmarshal without any problems until I came across a situation where the XML tag name is dynamic.
XML Could look like:
<unit_amount_in_cents>
<USD type="integer">4000</USD>
</unit_amount_in_cents>
<setup_fee_in_cents>
<USD type="integer">4000</USD>
</setup_fee_in_cents>
or
<unit_amount_in_cents>
<GBP type="integer">4000</GBP>
</unit_amount_in_cents>
<setup_fee_in_cents>
<GBP type="integer">4000</GBP>
</setup_fee_in_cents>
or could have both (or more)
<unit_amount_in_cents>
<USD type="integer">4000</USD>
<GBP type="integer">4000</GBP>
</unit_amount_in_cents>
<setup_fee_in_cents>
<USD type="integer">4000</USD>
<GBP type="integer">4000</USD>
</setup_fee_in_cents>
I can marshal to xml w/o problems by assigning the XML.Name.Local to what I need it to be but can't unmarshal it.
Here is what the struct looks like
type Plan struct {
XMLName xml.Name `xml:"plan"`
Name string `xml:"name,omitempty"`
PlanCode string `xml:"plan_code,omitempty"`
Description string `xml:"description,omitempty"`
SuccessUrl string `xml:"success_url,omitempty"`
CancelUrl string `xml:"cancel_url,omitempty"`
DisplayDonationAmounts bool `xml:"display_donation_amounts,omitempty"`
DisplayQuantity bool `xml:"display_quantity,omitempty"`
DisplayPhoneNumber bool `xml:"display_phone_number,omitempty"`
BypassHostedConfirmation bool `xml:"bypass_hosted_confirmation,omitempty"`
UnitName string `xml:"unit_name,omitempty"`
PaymentPageTOSLink string `xml:"payment_page_tos_link,omitempty"`
PlanIntervalLength int `xml:"plan_interval_length,omitempty"`
PlanIntervalUnit string `xml:"plan_interval_unit,omitempty"`
AccountingCode string `xml:"accounting_code,omitempty"`
CreatedAt *time.Time `xml:"created_at,omitempty"`
SetupFeeInCents CurrencyArray `xml:"setup_fee_in_cents,omitempty"`
UnitAmountInCents CurrencyArray `xml:"unit_amount_in_cents,omitempty"`
}
type CurrencyArray struct {
CurrencyList []Currency
}
func (c *CurrencyArray) AddCurrency(currency string, amount int) {
newc := Currency{Amount:fmt.Sprintf("%v",amount)}
newc.XMLName.Local = currency
c.CurrencyList = append(c.CurrencyList, newc)
}
func (c *CurrencyArray) GetCurrencyValue(currency string) (value int, e error) {
for _, v := range c.CurrencyList {
if v.XMLName.Local == currency {
value, e = strconv.Atoi(v.Amount)
return
}
}
e = errors.New(fmt.Sprintf("%s not found",currency))
return
}
type Currency struct {
XMLName xml.Name `xml:""`
Amount string `xml:",chardata"`
}
You need the tag xml:",any" on your CurrencyList field.
http://play.golang.org/p/i23w03z6R4