在使用Go生成XML文件时,如何创建文档类型声明?

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

When generating an XML file with Go, how do you create a doctype declaration?

问题

Go的xml包非常出色,使得处理XML变得非常容易。有一件事我不确定如何做:当从本地结构创建XML文档时,如何指定文档类型(doctype)?

例如,这些结构体:

type Person struct {
    XMLName    xml.Name `xml:"person"`
    FirstName  string   `xml:"firstName"`
    MiddleName string   `xml:"middleName"`
    LastName   string   `xml:"lastName"`
    Age        int64    `xml:"age"`
    Skills     []Skill  `xml:"skills"`
}

type Skill struct {
    XMLName        xml.Name `xml:"skill"`
    Name           string   `xml:"skillName"`
    YearsPracticed int64    `xml:"practice"`
}

将生成类似于以下的XML:

<person>
    <firstName>Bob</firstName>
    <middleName></middleName>
    <lastName>Jones</lastName>
    <age>23</age>
    <skills>
        <skill>
            <skillName>Cooking</skillName>
            <practice>3</practice>
        </skill>
        <skill>
            <skillName>Basketball</skillName>
            <practice>4</practice>
        </skill>
    </skills>
</person>

这很好,但是我该如何得到以下结果:

<?xml version="1.0" encoding="UTF-8"?>
<person>
    <firstName>Bob</firstName>
    <middleName></middleName>
    ...

这似乎太简单了,但是这是通过字符串拼接来实现的吗?

另外,Go的XML解析器如何处理你想要解组成一组结构体的文本块中的文档类型(doctype)?会忽略它吗?

英文:

Go's xml package is excellent and makes dealing with XML very easy. There's one thing I'm not sure how to do: when creating an XML document from a native struct, how do you specify the doctype?

For example, these structs:

type Person struct {
    XMLName    xml.Name `xml:&quot;person&quot;`
    FirstName  string   `xml:&quot;firstName&quot;`
    MiddleName string   `xml:&quot;middleName&quot;`
    LastName   string   `xml:&quot;lastName&quot;`
    Age        int64    `xml:&quot;age&quot;`
    Skills     []Skill  `xml:&quot;skills&quot;`
}

type Skill struct {
    XMLName        xml.Name `xml:&quot;skill&quot;`
    Name           string   `xml:&quot;skillName&quot;`
    YearsPracticed int64    `xml:&quot;practice&quot;`
}

Will generate something like this XML:

&lt;person&gt;
    &lt;firstName&gt;Bob&lt;/firstName&gt;
    &lt;middleName&gt;&lt;/middleName&gt;
    &lt;lastName&gt;Jones&lt;/middleName&gt;
    &lt;age&gt;23&lt;/age&gt;
    &lt;skills&gt;
        &lt;skill&gt;
            &lt;skillName&gt;Cooking&lt;/skillName&gt;
            &lt;practice&gt;3&lt;/practice&gt;
        &lt;/skill&gt;
        &lt;skill&gt;
            &lt;skillName&gt;Basketball&lt;/skillName&gt;
            &lt;practice&gt;4&lt;/practice&gt;
        &lt;/skill&gt;
    &lt;/skills&gt;
&lt;/person&gt;

Which is great, but what do I do to get this:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;person&gt;
    &lt;firstName&gt;Bob&lt;/firstName&gt;
    &lt;middleName&gt;&lt;/middleName&gt;
    ...

It almost seems too simple, but is this a matter of doing a string append?

And, on the reverse, how would Go's XML parser handle a doctype in a block of text that you wanted to unmarshal into a set of structs? Ignore it?

答案1

得分: 35

只需将您的编组字节附加到标头即可。如XML Package Source中所示,包含了一个通用的标头:

const (
    // 适用于与Marshal的输出一起使用的通用XML标头。
    // 这不会自动添加到此包的任何输出中,
    // 它只是作为一种便利提供。
    Header = `&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;` + &quot;\n&quot;
)

因此,您可以这样做:

myString, err := xml.MarshalIndent(...) // 这里是缩写
myString = []byte(xml.Header + string(myString))

我找到了一个可工作的示例(不是我的代码),可以在此处找到:http://play.golang.org/p/Rbfb717tvh

英文:

Simply append your marshalled bytes to the header. As seen in the XML Package Source, a generic header is included:

const (
    // A generic XML header suitable for use with the output of Marshal.
    // This is not automatically added to any output of this package,
    // it is provided as a convenience.
    Header = `&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;` + &quot;\n&quot;
)

So, this would do it:

myString, err := xml.MarshalIndent(...) // abbreviated here
myString = []byte(xml.Header + string(myString))

A working example I'd found (not my code) available at: http://play.golang.org/p/Rbfb717tvh

答案2

得分: 7

另一种值得一提的方法是使用Go语言的原生XML编码器,它比常见的marshal函数更适合处理XML流或者处理较大的XML文件。

在这种情况下,你需要先创建要编码的结构体对象,然后创建*File引用,并将xml.Header写入其中:

xmlFile, err := os.Create("my-file.xml")
if err != nil {
    fmt.Println("Error creating XML file: ", err)
    return
}
xmlFile.WriteString(xml.Header)

之后,你需要创建XML编码器,设置缩进,并将需要的结构体编码到XML文件中:

encoder := xml.NewEncoder(xmlFile)
encoder.Indent("", "\t")
err = encoder.Encode(&myStruct)
if err != nil {
    fmt.Println("Error encoding XML to file: ", err)
    return
}

然后,你应该能够看到生成的XML文件中包含你想要的文档头。

以下是使用你提供的示例进行的快速演示:

package main

import (
    "encoding/xml"
    "fmt"
    "os"
)

// Person 表示XML中的<person>节点
type Person struct {
    XMLName    xml.Name `xml:"person"`
    FirstName  string   `xml:"firstName"`
    MiddleName string   `xml:"middleName"`
    LastName   string   `xml:"lastName"`
    Age        int64    `xml:"age"`
    Skills     []Skill  `xml:"skills"`
}

// Skill 表示XML中的<skill>节点
type Skill struct {
    XMLName        xml.Name `xml:"skill"`
    Name           string   `xml:"skillName"`
    YearsPracticed int64    `xml:"practice"`
}

func main() {
    person := Person{
        FirstName:  "Bob",
        MiddleName: "",
        LastName:   "Jones",
        Age:        23,
        Skills: []Skill{
            {
                Name:           "Cooking",
                YearsPracticed: 3,
            },
            {
                Name:           "Basketball",
                YearsPracticed: 4,
            },
        },
    }

    xmlFile, err := os.Create("person.xml")
    if err != nil {
        fmt.Println("Error creating XML file: ", err)
        return
    }
    xmlFile.WriteString(xml.Header)
    encoder := xml.NewEncoder(xmlFile)
    encoder.Indent("", "\t")
    err = encoder.Encode(&person)
    if err != nil {
        fmt.Println("Error encoding XML to file: ", err)
        return
    }
}

这将生成以下XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<person>
    <firstName>Bob</firstName>
    <middleName></middleName>
    <lastName>Jones</lastName>
    <age>23</age>
    <skill>
        <skillName>Cooking</skillName>
        <practice>3</practice>
    </skill>
    <skill>
        <skillName>Basketball</skillName>
        <practice>4</practice>
    </skill>
</person>
英文:

Another approach that might worth mentioning here is the one that uses the native XML encoders from Go, which are more suitable than the common marshal function when dealing with streams of XML, or when you plan to work with pretty large XML files.

In this case you would follow a similar approach by creating the struct objects you want to encode first, but now the 2nd step will be creating the *File reference and then writing the xml.Header to it:

xmlFile, err := os.Create(&quot;my-file.xml&quot;)
if err != nil {
	fmt.Println(&quot;Error creating XML file: &quot;, err)
	return
}
xmlFile.WriteString(xml.Header)

After that you will need to create your XML encoder, set up its indentation, and finally encoding the necessary structs into the XML file:

encoder := xml.NewEncoder(xmlFile)
encoder.Indent(&quot;&quot;, &quot;\t&quot;)
err = encoder.Encode(&amp;myStruct)
if err != nil {
	fmt.Println(&quot;Error encoding XML to file: &quot;, err)
	return
}

You should see the resulting XML file with the document header you want after that.

Here is a quick POF by using the samples you provided:

package main

import (
	&quot;encoding/xml&quot;
	&quot;fmt&quot;
	&quot;os&quot;
)

// Person represents a &lt;person&gt; node in the XML
type Person struct {
	XMLName    xml.Name `xml:&quot;person&quot;`
	FirstName  string   `xml:&quot;firstName&quot;`
	MiddleName string   `xml:&quot;middleName&quot;`
	LastName   string   `xml:&quot;lastName&quot;`
	Age        int64    `xml:&quot;age&quot;`
	Skills     []Skill  `xml:&quot;skills&quot;`
}

// Skill represents a &lt;skill&gt; node in the XML
type Skill struct {
	XMLName        xml.Name `xml:&quot;skill&quot;`
	Name           string   `xml:&quot;skillName&quot;`
	YearsPracticed int64    `xml:&quot;practice&quot;`
}

func main() {
	person := Person{
		FirstName:  &quot;Bob&quot;,
		MiddleName: &quot;&quot;,
		LastName:   &quot;Jones&quot;,
		Age:        23,
		Skills: []Skill{
			{
				Name:           &quot;Cooking&quot;,
				YearsPracticed: 3,
			},
			{
				Name:           &quot;Basketball&quot;,
				YearsPracticed: 4,
			},
		},
	}

	xmlFile, err := os.Create(&quot;person.xml&quot;)
	if err != nil {
		fmt.Println(&quot;Error creating XML file: &quot;, err)
		return
	}
	xmlFile.WriteString(xml.Header)
	encoder := xml.NewEncoder(xmlFile)
	encoder.Indent(&quot;&quot;, &quot;\t&quot;)
	err = encoder.Encode(&amp;person)
	if err != nil {
		fmt.Println(&quot;Error encoding XML to file: &quot;, err)
		return
	}
}

And this will generate the following XML file:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;person&gt;
	&lt;firstName&gt;Bob&lt;/firstName&gt;
	&lt;middleName&gt;&lt;/middleName&gt;
	&lt;lastName&gt;Jones&lt;/lastName&gt;
	&lt;age&gt;23&lt;/age&gt;
	&lt;skill&gt;
		&lt;skillName&gt;Cooking&lt;/skillName&gt;
		&lt;practice&gt;3&lt;/practice&gt;
	&lt;/skill&gt;
	&lt;skill&gt;
		&lt;skillName&gt;Basketball&lt;/skillName&gt;
		&lt;practice&gt;4&lt;/practice&gt;
	&lt;/skill&gt;
&lt;/person&gt;

huangapple
  • 本文由 发表于 2014年10月15日 07:14:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/26371965.html
匿名

发表评论

匿名网友

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

确定