拆封<标签值=“VAL” />标签来在围棋串

问题描述:

说我已经有了一个围棋结构定义如下:拆封<标签值=“VAL” />标签来在围棋串

type MyType struct { 
    FieldA string 
    FieldB string 
    FIeldC string 
} 

,并与其对应的,看起来像这样的XML:

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

其中FieldA和FieldB是强制性的,而FieldC是可选的。如何指定结构标签以从“value”属性获取字段的值?此:

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

生成“XML:FIELDA>价值链无效使用attr标记”和这样的:

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

不产生错误,但没有找到字段的值。

为了支持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

这是一个很好的折衷,只改变struct元素类型的_name_,同时仍然能够处理XML的怪异。我会进一步玩。谢谢! –

+0

我已经在实际代码中实现了您的解决方案。唯一的缺点是你不能从需要字符串的函数中返回一个字段值,但这是一个小问题。在实际的代码中,我使用了StringField,BooleanField等。这真是一个很好的解决方案,谢谢。 –

+0

@ScottDeerwester乐于帮助!也可以,只需将其转换为字符串:'func getStr(f Field)string {return string(f)}'。它毕竟是一个字符串的别名。 – OneOfOne

您不能使用fieldA>value的形式,因为此“路径”的元素表示元素,并且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:} 

尝试该变型(与JSON)上Go Playground

+0

感谢您的快速答复。这里的问题是我需要能够从JSON或XML解组。在JSON中,格式为“{”fieldA“:”apple“,”fieldB“:”banana“}。从逻辑上讲,价值属于父母,而不是孩子,所以我没有引入包装的*。 –

+0

@ScottDeerwester你没有在你的问题中提及任何与json相关的东西。如果你想用一个结构来处理XML和JSON,那么使用元素内容来保存值,而不是它的属性。 – icza

+0

我很抱歉,不提JSON的原因是简洁。我真的没有改变结构定义或XML格式的*。后者来自[FHIR标准](http://hl7-fhir.github.io/),前者来自[Go标准版本](https://github.com/intervention-engine/fhir) 。 –

你可以通过引入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:""}} 

你可以找到derserializing在golang.org/src/encoding/xml/example_test.go在旅途中源XML属性的其他例子。