Write XML code to XML file in Go

huangapple go评论78阅读模式
英文:

Write XML code to XML file in Go

问题

我有一个打印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文件,并将代码写入该文件呢?

英文:

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(&quot;&lt;card entity=\&quot;%s\&quot;&gt;\n&quot;, id)
    fmt.Println(&quot;  &lt;facts&gt;&quot;)
    for k, v := range properties {
	    for _,value := range v {
    	    fmt.Printf(&quot;    &lt;fact property=\&quot;%s\&quot;&gt;%s&lt;/fact&gt;\n&quot;, k, value)
	    }
    }
    fmt.Println(&quot;  &lt;/facts&gt;&quot;)
    fmt.Println(&quot;&lt;/card&gt;&quot;)
}

How can I get it to write an XML file and then write the code to that XML file?

答案1

得分: 19

使用encoding/xml包来处理XML可能更好。在Go中,可以定义如下的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"`
}

可以按照以下方式创建数据结构(在playground上运行示例):

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)
}
英文:

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:&quot;entity,attr&quot;`
	Facts  Facts
}

type Facts struct {
	Fact []Fact
}

type Fact struct {
	Property string `xml:&quot;property,attr&quot;`
	Value string `xml:&quot;,innerxml&quot;`
}

Create your data structure like this (running example on play):

card := &amp;Card{
	Entity: &quot;1234id&quot;,
	Facts: Facts{[]Fact{
		Fact{Property: &quot;prop1&quot;, Value: &quot;val1&quot;},
		Fact{Property: &quot;prop2&quot;, Value: &quot;val2&quot;},
	}},
}

Now you can encode the structure to XML and write it directly to a io.Writer:

writer, err := os.Open(&quot;/tmp/tmp.xml&quot;)

encoder := xml.NewEncoder(writer)
err := encoder.Encode(data)

if err != nil { panic(err) }

答案2

得分: 3

使用os.Create函数打开文件,并使用fmt.Fprintf函数向文件中写入内容。

示例代码:

f, err := os.Create("out.xml") // 创建/截断文件
if err != nil { panic(err) } // 如果出错则抛出异常
defer f.Close() // 确保文件在使用后关闭

fmt.Fprintf(f, "<card entity=\"%s\">\n", id)
// ...

请注意,以上代码是使用Go语言编写的示例代码,用于打开文件并向其中写入内容。

英文:

Use <a href="http://golang.org/pkg/os/#Create">os.Create</a> to open the file and use <a href="http://golang.org/pkg/fmt/#Fprintf">fmt.Fprintf</a> to write to it.

Example:

f, err := os.Create(&quot;out.xml&quot;) // create/truncate the file
if err != nil { panic(err) } // panic if error
defer f.Close() // make sure it gets closed after

fmt.Fprintf(f, &quot;&lt;card entity=\&quot;%s\&quot;&gt;\n&quot;, id)
// ...

答案3

得分: 2

为了补充bgp的正确答案,通过将函数更改为接受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>")
}

输出到屏幕(Stdout):

processTopic(os.Stdout, id, properties)

写入文件(代码取自bgp的答案):

f, err := os.Create("out.xml") // 创建/截断文件
if err != nil { panic(err) } // 如果出错则抛出异常
defer f.Close() // 确保在之后关闭文件
processTopic(f, id, properties)
英文:

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, &quot;&lt;card entity=\&quot;%s\&quot;&gt;\n&quot;, id)
    fmt.Fprintln(w, &quot;  &lt;facts&gt;&quot;)
    for k, v := range properties {
        for _,value := range v {
            fmt.Fprintf(w, &quot;    &lt;fact property=\&quot;%s\&quot;&gt;%s&lt;/fact&gt;\n&quot;, k, value)
        }
    }
    fmt.Fprintln(w, &quot;  &lt;/facts&gt;&quot;)
    fmt.Fprintln(w, &quot;&lt;/card&gt;&quot;)
}

Printing to screen (Stdout):

processTopic(os.Stdout, id, properties)

Writing to file (code taken from bgp's answer):

f, err := os.Create(&quot;out.xml&quot;) // 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)

答案4

得分: 0

根据nemo的评论,根据你接收到的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 the 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)
	}
}
英文:

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 (
&quot;encoding/xml&quot;
&quot;io&quot;
&quot;os&quot;
)
type FactMap map[string]string
type factXml struct {
XMLName xml.Name `xml:&quot;fact&quot;`
Prop string `xml:&quot;property,attr&quot;`
Value string `xml:&quot;,innerxml&quot;`
}
// 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(&amp;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:&quot;card&quot;`
Entity int `xml:&quot;entity,attr&quot;`
Facts FactMap `xml:&quot;facts&quot;`
}
func main() {
props1 := map[string]string{&quot;key1&quot;: &quot;val1&quot;, &quot;key2&quot;: &quot;val2&quot;}
// 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(&quot;my.xml&quot;)
if err != nil {
// if the file doesn&#39;t exist, open it with write and create flags
f, err = os.OpenFile(&quot;my.xml&quot;, os.O_WRONLY|os.O_CREATE, 0666)
} else {
// if the file does exist, open it with append and write flags
f, err = os.OpenFile(&quot;my.xml&quot;, 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)
}
}

huangapple
  • 本文由 发表于 2013年10月8日 10:30:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/19238114.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定