我有一个打印行代码的脚本,但我需要它来编写一个新的XML文件,然后将XML代码写入文件而不是打印它。
这是打印XML代码的函数
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>")
}
如何让它编写XML文件,然后将代码写入该XML文件?
答案 0 :(得分:14)
打印XML时可能没问题,为什么不使用encoding/xml
包呢?
你的XML结构是:
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"`
}
像这样创建数据结构(running example on play):
card := &Card{
Entity: "1234id",
Facts: Facts{[]Fact{
Fact{Property: "prop1", Value: "val1"},
Fact{Property: "prop2", Value: "val2"},
}},
}
现在,您可以将结构编码为XML并将其直接写入io.Writer
:
writer, err := os.Open("/tmp/tmp.xml")
encoder := xml.NewEncoder(writer)
err := encoder.Encode(data)
if err != nil { panic(err) }
答案 1 :(得分:3)
使用os.Create打开文件并使用fmt.Fprintf写入文件。
示例:
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)
// ...
答案 2 :(得分:2)
添加到bgp的(+1)正确答案;通过更改函数以将io.Writer作为参数,您可以将XML输出到实现io.Writer接口的任何类型的输出。
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>")
}
打印到屏幕(标准输出):
processTopic(os.Stdout, id, properties)
写入文件(代码取自bgp的答案):
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)
答案 3 :(得分:0)
支持nemo对encoding/xml
的评论;根据您收到fact
数据的方式,如果收到的是map[string]string
,那么您还可以为fact
地图创建一个Marshaler和Unmarshaler。如果您处理的是未按顺序接收的较大数据集(即无序映射与有序数组/切片),则稍微复杂一点可能会有所帮助。
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)
}
}