Golang:Unmarshal Self Closing Tags

时间:2014-05-18 17:04:41

标签: xml go unmarshalling

因此,我尝试解组由Google Go中的其他程序生成的保存文件生成的XML文件。它似乎很好,因为关于这方面的文档相当广泛:http://golang.org/pkg/encoding/xml/#Unmarshal

我仍遇到问题。保存文件中的输出如下:

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

而不是承诺,位置也可能是紧急的,也可能都不是。这些位置也可以有一个名称和不同的标签,但这些似乎解析得很好。 在我的Go代码中,我使用以下结构:

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

虽然encoding / xml包的Unmarshal函数运行没有错误,并且数据中显示了所示的示例,但是commit和urgent的所有值都是&#34; false&#34;。

为了获得这两个字段的正确值,我应该更改什么?

(使用以下代码完成解组)

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)

4 个答案:

答案 0 :(得分:10)

根据this discussion,不支持此行为,您唯一没有看到错误的原因是您在结构定义中错误committed。如果你正确地写它会得到一个解析错误,因为一个空字符串(一个封闭标签的内容)不是一个布尔值(example on play)。

在链接的golang-nuts线程中,rsc建议使用*struct{}(指向空结构的指针)并检查该值是否为nilexample on play):

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

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

答案 1 :(得分:2)

对于简单的布尔值,即当元素存在时value为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没有消耗整个元素

编辑:@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来比较布尔值。

答案 2 :(得分:0)

使用一种简单的方法来解决这个空自闭标签或空标签的问题 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

答案 3 :(得分:0)

我的情况有所不同:我在golang中创建了一个应用,作为从api-a提交到api-b的转发器。 虽然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。