Golang:解析自闭合标签

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

Golang: Unmarshal Self Closing Tags

问题

所以我正在尝试在Google Go中解析由另一个程序生成的XML保存文件。这方面的文档非常详尽:http://golang.org/pkg/encoding/xml/#Unmarshal

但是我遇到了一个问题。保存文件中的输出如下所示:

<location id="id4" x="-736" y="-544">
    <committed />
</location>

除了committed之外,location还可以是urgent或者既不是committed也不是urgent。location还可以有名称和不同的标签,但是这些似乎解析得很好。
在我的Go代码中,我使用以下结构体:

type Location struct {
    Id string `xml:"id,attr"`
    Committed bool `xml:"committed"`
    Urgent bool `xml:"urgent"`
    Labels []Label `xml:"label"`
}

尽管encoding/xml包的Unmarshal函数运行没有错误,并且数据中显示的示例也存在,但是committed和urgent的所有值都是"false"。

我应该如何更改才能获得这两个字段的正确值?

(使用以下代码片段进行解组)

xmlFile, err := os.Open("model.xml")
if err != nil {
    fmt.Println("Error opening file:", err)
    return
}
defer xmlFile.Close()

b, _ := ioutil.ReadAll(xmlFile)

var xmlScheme uppaal.UppaalXML
err = xml.Unmarshal(b, &xmlScheme)
fmt.Println(err)
英文:

So I'm trying to Unmarshal an XML file generated as a save file by another program in Google Go. It seems to be going great as the documentation on this is quite extensive: http://golang.org/pkg/encoding/xml/#Unmarshal

Still I'm running into a problem. The output in the save file is like this:

&lt;location id=&quot;id4&quot; x=&quot;-736&quot; y=&quot;-544&quot;&gt;
    &lt;committed /&gt;
&lt;/location&gt;

Instead of committed, a location can also be urgent or neither. The locations can also have a name and different labels, but these seem to be parsing just fine.
In my Go code I'm using the following struct:

type Location struct {
    Id string `xml:&quot;id,attr&quot;`
    Committed bool `xml:&quot;commited&quot;`
    Urgent bool `xml:&quot;urgent&quot;`
    Labels []Label `xml:&quot;label&quot;`
}

And although the Unmarshal function of the encoding/xml package runs without errors and the shown example is present in the data, all the values of both committed and urgent are "false".

What should I change to get the right values for these two fields?

(Unmarshalling is done using the following piece of code)

xmlFile, err := os.Open(&quot;model.xml&quot;)
if err != nil {
	fmt.Println(&quot;Error opening file:&quot;, err)
	return
}
defer xmlFile.Close()

b, _ := ioutil.ReadAll(xmlFile)

var xmlScheme uppaal.UppaalXML
err = xml.Unmarshal(b, &amp;xmlScheme)
fmt.Println(err)

答案1

得分: 12

根据这个讨论,不支持这种行为,你没有看到错误的唯一原因是在结构定义中拼写错误了committed。如果你正确书写,你将会得到一个解析错误,因为一个空字符串(封闭标签的内容)不是一个布尔值(play上的示例)。

在链接的golang-nuts线程中,rsc建议使用*struct{}(一个指向空结构体的指针),并检查该值是否为nilplay上的示例):

type Location struct {
    Id        string    `xml:"id,attr"`
    Committed *struct{} `xml:"committed"`
    Urgent    bool      `xml:"urgent"`
}

if l.Committed != nil {
    // 处理未提交的情况
}
英文:

According to this discussion this behaviour is not supported and the only reason you don't see an error is that you mispelled committed in the struct definition. If you write it correctly you will get a parse error because an empty string (the contents of a closed tag) is not a boolean value (example on play).

In the linked golang-nuts thread rsc suggests to use *struct{} (a pointer to an empty struct) and check if that value is nil (example on play):

type Location struct {
	Id        string    `xml:&quot;id,attr&quot;`
	Committed *struct{} `xml:&quot;committed&quot;`
	Urgent    bool      `xml:&quot;urgent&quot;`
}

if l.Committed != nil {
	// handle not committed
}

答案2

得分: 4

对于简单的布尔值,即当元素存在时值为true,我是这样解决的:

示例XML:

<struct>
    <hide/>
    <data>Value</data>
</struct>

Go中的数据结构:

type XMLValues struct {
    Hide BoolIfElementPresent `xml:"hide"`
    Data string               `xml:"data"`
}

type BoolIfElementPresent struct {
    bool
}

func (c *BoolIfElementPresent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    var v string
    d.DecodeElement(&v, &start)
    *c = BoolIfElementPresent{true}
    return nil
}

这样,每当存在<hide/>并尝试解组时,它就会返回true。如果<hide/>不存在,则不会尝试解组,结构体中的默认值false保持不变。

请注意,每次使用时都必须将布尔值包装在自定义结构体中。

d.DecodeElement(&v, &start)部分似乎是不必要的,但如果省略该代码片段,您将收到错误消息:

xml: (*mypackage.BoolIfElementPresent).UnmarshalXML did not consume entire element

**编辑:**由@ShogunPanda提供的简化版本:

type XMLValues struct {
    Hide BoolIfElementPresent `xml:"hide"`
    Data string               `xml:"data"`
}

type BoolIfElementPresent bool

func (c *BoolIfElementPresent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    var v string
    d.DecodeElement(&v, &start)
    *c = true
    return nil
}

请注意,您必须使用if xyz == true来比较布尔值。

英文:

For simple boolean values, i.e. value is true when an element is present, I have solved it this way:

Example XML:

&lt;struct&gt;
    &lt;hide/&gt;
    &lt;data&gt;Value&lt;/data&gt;
&lt;/struct&gt;

Data structure in Go:

type XMLValues struct {
    Hide BoolIfElementPresent `xml:&quot;hide&quot;`
    Data string               `xml:&quot;data&quot;`
}

type BoolIfElementPresent struct {
	bool
}

func (c *BoolIfElementPresent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	var v string
	d.DecodeElement(&amp;v, &amp;start)
	*c = BoolIfElementPresent{true}
	return nil
}

This way, whenever &lt;hide/&gt; is present and unmarshalling is attempted, it just returns true. If &lt;hide/&gt; is not present, no unmarshalling is attempted and the default value false remains in the struct.

Note that you have to wrap your boolean values in the custom struct every time you work with it, though.

The d.DecodeElement(&amp;v, &amp;start) part seems unnecessary, but if you omit that piece of code you will receive an error message:

> xml: (*mypackage.BoolIfElementPresent).UnmarshalXML did not consume entire <hide> element

Edit: Simplified version curtesy of @ShogunPanda:

type XMLValues struct {
    Hide BoolIfElementPresent `xml:&quot;hide&quot;`
    Data string               `xml:&quot;data&quot;`
}

type BoolIfElementPresent bool

func (c *BoolIfElementPresent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	var v string
	d.DecodeElement(&amp;v, &amp;start)
	*c = true
	return nil
}

Note that you have to compare the booleans by using if xyz == true.

答案3

得分: 1

解决空自闭合标签或空标签的一个简单方法是使用https://github.com/guregu/null包。

我不太喜欢为小事情使用包,但是这个包为我节省了很多时间。

以下是我如何使用它的示例代码:

package main

import (
	"encoding/xml"
	"fmt"

	"github.com/guregu/null"
)

func main() {
	type Result struct {
		XMLName xml.Name   `xml:"Person"`
		Name    string     `xml:"FullName"`
		Id      null.Int   `xml:"Id"`
		Height  null.Float `xml:"Height"`
	}
	v := Result{}

	data := `
		<Person>
			<FullName>Grace R. Emlin</FullName>
			<Id></Id>
			<Height />
		</Person>
	`
	err := xml.Unmarshal([]byte(data), &v)

	if err != nil {
		fmt.Printf("error: %v", err)
		return
	}

	fmt.Printf("XMLName: %#v\n", v.XMLName)
	fmt.Printf("Name: %q\n", v.Name)

	if !v.Id.IsZero() {
		fmt.Printf("Id: %d\n", v.Id.Int64)
	}
}

http://play.golang.org/p/xGKeIUM6NO

全部功劳归给guregu/null。

英文:

An easy way to solve this issue of empty self closing tags or empty tag is to use
https://github.com/guregu/null package.

I am not a big fan of using packages for small stuff but this package have saved a lot of time for me

Here is how I am using this

package main

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

	&quot;github.com/guregu/null&quot;
)

func main() {
	type Result struct {
		XMLName xml.Name   `xml:&quot;Person&quot;`
		Name    string     `xml:&quot;FullName&quot;`
		Id      null.Int   `xml:&quot;Id&quot;`
		Height  null.Float `xml:&quot;Height&quot;`
	}
	v := Result{}

	data := `
		&lt;Person&gt;
			&lt;FullName&gt;Grace R. Emlin&lt;/FullName&gt;
			&lt;Id&gt;&lt;/Id&gt;
			&lt;Height /&gt;
		&lt;/Person&gt;
	`
	err := xml.Unmarshal([]byte(data), &amp;v)

	if err != nil {
		fmt.Printf(&quot;error: %v&quot;, err)
		return
	}

	fmt.Printf(&quot;XMLName: %#v\n&quot;, v.XMLName)
	fmt.Printf(&quot;Name: %q\n&quot;, v.Name)

	if !v.Id.IsZero() {
		fmt.Printf(&quot;Id: %d\n&quot;, v.Id.Int64)
	}
}

http://play.golang.org/p/xGKeIUM6NO

Full credit to guregu/null

答案4

得分: -1

我的案例有所不同:我创建了一个用于从api-a提交到api-b的golang应用程序作为转发器。
当api-a生成一个未关闭的标签时,golang将无法读取它,这将导致api-b无法读取它。因此,我创建了两个类,一个是读取器,另一个是发送器。

也许这对其他人有所启发。

package main

import (
	"encoding/xml"
	"fmt"
)

type MyDataReader struct {
	Name  string    `xml:"name"`
	Lulus *struct{} `xml:"lulus"`
}

type MyDataPoster struct {
	Name  string `xml:"name"`
	Lulus string `xml:"lulus"`
}

func ToPoster(m MyDataReader) MyDataPoster {
	res := MyDataPoster{}
	res.Name = m.Name
	if m.Lulus != nil {
		res.Lulus = "1"
	}
	return res
}

func main() {
	xmlString := `<data>
		<name>richard</name>
		<lulus />
	</data>`

	m := MyDataReader{}
	err := xml.Unmarshal([]byte(xmlString), &m)
	fmt.Println(err, m)

	mpost := ToPoster(m)
	output, errenc := xml.MarshalIndent(mpost, "", "    ")
	fmt.Println(errenc, string(output))
}

我创建了两个类。一个用于检索和解析,另一个用于提交XML数据。

英文:

My case is different: I create app in golang as a forwarder to submit from api-a to api-b.
While api-a may generating a tag without closing and golang will not able to read it, which will not be read by api-b. Therefore I create two classes which one is reader and other is poster(sender).

maybe this will give some insight to others.

package main

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

type MyDataReader struct {
	Name string `xml:&quot;name&quot;`
	Lulus *struct{} `xml:&quot;lulus&quot;`
}

type MyDataPoster struct {
	Name string `xml:&quot;name&quot;`
	Lulus string `xml:&quot;lulus&quot;`
}

func ToPoster(m MyDataReader) MyDataPoster {
	res := MyDataPoster{}
	res.Name = m.Name
	if m.Lulus != nil {
		res.Lulus = &quot;1&quot;
	}
	return res
}

func main() {
	xmlString := `&lt;data&gt;
		&lt;name&gt;richard&lt;/name&gt;
		&lt;lulus /&gt;
	&lt;/data&gt;`


	m := MyDataReader{}
	err := xml.Unmarshal([]byte(xmlString), &amp;m)
	fmt.Println(err, m)

	mpost := ToPoster(m)
	output, errenc := xml.MarshalIndent(mpost, &quot;&quot;, &quot;    &quot;)
	fmt.Println(errenc, string(output))
}

I create two class. One for retrieving and parsing and one for submit the xml.

huangapple
  • 本文由 发表于 2014年5月19日 01:04:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/23724591.html
匿名

发表评论

匿名网友

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

确定