Reputation: 691
I have a script that print-lines XML code, but I need it to write a new XML file and then write the XML code to the file instead of printing it.
Here is the function that prints the XML code
func processTopic(id string, properties map[string][]string) {
fmt.Printf("<card entity=\"%s\">\n", id)
fmt.Println(" <facts>")
for k, v := range properties {
for _,value := range v {
fmt.Printf(" <fact property=\"%s\">%s</fact>\n", k, value)
}
}
fmt.Println(" </facts>")
fmt.Println("</card>")
}
How can I get it to write an XML file and then write the code to that XML file?
Upvotes: 5
Views: 7338
Reputation: 924
Supporting nemo's comment on the encoding/xml
; depending on how you're receiving the fact
data, if it is received as a map[string]string
then you can also create a Marshaler and Unmarshaler for the fact
map. This is slightly more complexcan be helpful if you're dealing with larger sets of data that you're not receiving in an ordered manner (ie. unordered map vs ordered array/slice).
package main
import (
"encoding/xml"
"io"
"os"
)
type FactMap map[string]string
type factXml struct {
XMLName xml.Name `xml:"fact"`
Prop string `xml:"property,attr"`
Value string `xml:",innerxml"`
}
// Marshal the fact map
func (fm FactMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if len(fm) == 0 {
return nil
}
err := e.EncodeToken(start)
if err != nil {
return err
}
for k, v := range fm {
// XML encoding the `fact` XML entry
e.Encode(factXml{Prop: k, Value: v})
}
return e.EncodeToken(start.End())
}
// Unmarshal the fact map
func (fm *FactMap) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
*fm = FactMap{}
for {
var f factXml
err := d.Decode(&f)
if err == io.EOF {
break
} else if err != nil {
return err
}
(*fm)[f.Prop] = f.Value
}
return nil
}
// Note usage of xml.Name to set the outer XML to `card`, as well as setting Entity as an `entity,attr`
type Card struct {
XMLName xml.Name `xml:"card"`
Entity int `xml:"entity,attr"`
Facts FactMap `xml:"facts"`
}
func main() {
props1 := map[string]string{"key1": "val1", "key2": "val2"}
// Populate the Card struct and marshal it
card := Card{Entity: 5, Facts: props1}
// Append to the file
var f *os.File
// Check if thet file exists, err != nil if the file does not exist
_, err := os.Stat("my.xml")
if err != nil {
// if the file doesn't exist, open it with write and create flags
f, err = os.OpenFile("my.xml", os.O_WRONLY|os.O_CREATE, 0666)
} else {
// if the file does exist, open it with append and write flags
f, err = os.OpenFile("my.xml", os.O_APPEND|os.O_WRONLY, 0666)
}
if err != nil {
panic(err)
}
defer f.Close()
e := xml.NewEncoder(f)
// Write marshal the card struct to the file
err = e.Encode(card)
if err != nil {
panic(err)
}
}
Upvotes: 0
Reputation: 57757
While printing your XML may be fine, why not use the encoding/xml
package?
Have your XML structure in go:
type Card struct {
Entity string `xml:"entity,attr"`
Facts Facts
}
type Facts struct {
Fact []Fact
}
type Fact struct {
Property string `xml:"property,attr"`
Value string `xml:",innerxml"`
}
Create your data structure like this (running example on play):
card := &Card{
Entity: "1234id",
Facts: Facts{[]Fact{
Fact{Property: "prop1", Value: "val1"},
Fact{Property: "prop2", Value: "val2"},
}},
}
Now you can encode the structure to XML and write it directly to a io.Writer
:
writer, err := os.Open("/tmp/tmp.xml")
encoder := xml.NewEncoder(writer)
err := encoder.Encode(data)
if err != nil { panic(err) }
Upvotes: 19
Reputation: 78095
To add to bgp's (+1) correct answer; by changing the function to take a io.Writer as argument, you can output your XML to any type of output implementing the io.Writer interface.
func processTopic(w io.Writer, id string, properties map[string][]string) {
fmt.Fprintf(w, "<card entity=\"%s\">\n", id)
fmt.Fprintln(w, " <facts>")
for k, v := range properties {
for _,value := range v {
fmt.Fprintf(w, " <fact property=\"%s\">%s</fact>\n", k, value)
}
}
fmt.Fprintln(w, " </facts>")
fmt.Fprintln(w, "</card>")
}
Printing to screen (Stdout):
processTopic(os.Stdout, id, properties)
Writing to file (code taken from bgp's answer):
f, err := os.Create("out.xml") // create/truncate the file
if err != nil { panic(err) } // panic if error
defer f.Close() // make sure it gets closed after
processTopic(f, id, properties)
Upvotes: 3
Reputation: 11417
Use os.Create to open the file and use fmt.Fprintf to write to it.
Example:
f, err := os.Create("out.xml") // create/truncate the file
if err != nil { panic(err) } // panic if error
defer f.Close() // make sure it gets closed after
fmt.Fprintf(f, "<card entity=\"%s\">\n", id)
// ...
Upvotes: 4