拆封<标签值=“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.Unmarshaler
和xml.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
}
您不能使用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。
感谢您的快速答复。这里的问题是我需要能够从JSON或XML解组。在JSON中,格式为“{”fieldA“:”apple“,”fieldB“:”banana“}。从逻辑上讲,价值属于父母,而不是孩子,所以我没有引入包装的自由。 –
@ScottDeerwester你没有在你的问题中提及任何与json相关的东西。如果你想用一个结构来处理XML和JSON,那么使用元素内容来保存值,而不是它的属性。 – icza
我很抱歉,不提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属性的其他例子。
这是一个很好的折衷,只改变struct元素类型的_name_,同时仍然能够处理XML的怪异。我会进一步玩。谢谢! –
我已经在实际代码中实现了您的解决方案。唯一的缺点是你不能从需要字符串的函数中返回一个字段值,但这是一个小问题。在实际的代码中,我使用了StringField,BooleanField等。这真是一个很好的解决方案,谢谢。 –
@ScottDeerwester乐于帮助!也可以,只需将其转换为字符串:'func getStr(f Field)string {return string(f)}'。它毕竟是一个字符串的别名。 – OneOfOne