解组<tag value =“val”>到Go中的Tag字符串

时间:2016-04-30 14:56:28

标签: go xml-parsing

假设我的Go结构定义如下:

type MyType struct {
    FieldA string
    FieldB string
    FIeldC string
}

和对应于它的XML如下所示:

<obj>
    <fieldA value="apple"/>
    <fieldB value="banana"/>
</obj>

其中FieldA和FieldB是必需的,而FieldC是可选的。如何指定struct标签,以便从&#34;值&#34;中获取字段的值。属性?这样:

FieldA string `xml:"fieldA>value,attr"`
FieldB string `xml:"fieldB>value,attr"`
FieldC string `xml:"fieldC>value,attr,omitempty"`

使用attr标志生成&#34; xml:fieldA&gt;值链无效&#34;而这:

FieldA string `xml:"fieldA"`
FieldB string `xml:"fieldB"`
FieldC string `xml:"fieldC,omitempty"`

不会产生错误,但无法找到字段的值。

3 个答案:

答案 0 :(得分:1)

要同时支持XML和JSON,您必须定义一个简单类型并在其上实现xml.Unmarshalerxml.Marshaler接口,这是一个示例:

type Field string

func (f Field) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    v := struct {
        Value string `xml:"value,attr"`
    }{string(f)}
    return e.EncodeElement(v, start)
}

func (f *Field) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    var v struct {
        Value string `xml:"value,attr"`
    }
    err := d.DecodeElement(&v, &start)
    *f = Field(v.Value)
    return err
}

playground

答案 1 :(得分:0)

您无法使用fieldA>value的形式,因为此&#34;路径&#34;的元素表示元素,而value不是您案例中的元素。

如果要从子元素的属性中获取值,可以为其创建包装类型。

例如:

type Field struct {
    Value string `xml:"value,attr"`
}

使用此MyType结构:

type MyType struct {
    FieldA Field `xml:"fieldA"`
    FieldB Field `xml:"fieldB"`
    FieldC Field `xml:"fieldC"`
}

测试它:

func main() {
    mt := MyType{}
    if err := xml.Unmarshal([]byte(src), &mt); err != nil {
        panic(err)
    }
    fmt.Printf("%+v\n", mt)
}

const src = `<obj>
    <fieldA value="apple"/>
    <fieldB value="banana"/>
</obj>`

输出(在 Go Playground 上试试):

{FieldA:{Value:apple} FieldB:{Value:banana} FieldC:{Value:}}

修改

如果要使用一个结构处理XML和JSON,则应在XML中使用元素的内容来保存数据(而不是value属性),例如:

<obj>
    <fieldA>apple</fieldA>
    <fieldB>banana</fieldB>
</obj>

以及对此进行建模的结构:

type MyType struct {
    FieldA string `xml:"fieldA"`
    FieldB string `xml:"fieldB"`
    FieldC string `xml:"fieldC"`
}

这个结构可以从JSON解组:

const src2 = `{"fieldA": "apple", "fieldB": "banana"}`

mt = MyType{}
if err := json.Unmarshal([]byte(src2), &mt); err != nil {
    panic(err)
}
fmt.Printf("%+v\n", mt)

输出:相同:

{FieldA:apple FieldB:banana FieldC:}
{FieldA:apple FieldB:banana FieldC:}

Go Playground 上尝试此变体(使用JSON)。

答案 2 :(得分:0)

您可以通过引入具有Value成员的Field结构来实现:

type MyType struct {
    FieldA Field `xml:"fieldA"`
    FieldB Field `xml:"fieldB"`
    FIeldC Field `xml:"fieldC"`
}

type Field struct {
    Value string `xml:"value,attr"`
}

这应该可以解决问题。这是完整的例子:

package main

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

type MyType struct {
    FieldA Field `xml:"fieldA"`
    FieldB Field `xml:"fieldB"`
    FieldC Field `xml:"fieldC"`
}

type Field struct {
    Value string `xml:"value,attr"`
}

func deserializeMyType(reader io.Reader) (MyType, error) {
    myType := MyType{}
    decoder := xml.NewDecoder(reader)
    err := decoder.Decode(&myType)
    if err != nil {
        return MyType{}, err
    }

    return myType, nil
}

func main() {
    inputXML := `<obj>
    <fieldA value="apple"/>
    <fieldB value="banana"/>
</obj>`

    xmlReader := strings.NewReader(inputXML)
    myType, err := deserializeMyType(xmlReader)
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s", err.Error())
        os.Exit(1)
    }

    fmt.Fprintf(os.Stdout, "%#v\n", myType)
}

示例XML的输出将是:

main.MyType{FieldA:main.Field{Value:"apple"}, FieldB:main.Field{Value:"banana"}, FieldC:main.Field{Value:""}}

您可以在golang.org/src/encoding/xml/example_test.go的go源中找到其他用于驱动XML属性的示例。