在运行时更改结构标签(xml)

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

Change struct tag (xml) at runtime

问题

有一个结构体:

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

我想将这个结构体编码为一个XML文件。然而,我希望每个文件中Value的属性名都不同:

s1 := S{
  Value: "One"
}

应该编码为:

<S value="One"></S>

s2 := S{
  Value: "Two"
}

应该编码为:

<S category="Two"></S>

所以,我需要改变XML元素名字,或者改变字段上的标签。这可行吗?

我查看了reflect(https://golang.org/pkg/reflect/#Value.FieldByName),但是由于FieldByName返回一个值类型,并且没有Set方法,我认为不能使用反射来实现。

英文:

There's a struct:

type S struct {
  Value string `xml:&quot;value,attr&quot;`
}

I want to encode the struct to an XML file. However, I want the attribute name of Value to be different in each file:

s1 := S{
  Value: &quot;One&quot;
}

should encode to:

&lt;S value=&quot;One&quot;&gt;&lt;/S&gt;

and

s2 := S{
  Value: &quot;Two&quot;
}

should encode to:

&lt;S category=&quot;Two&quot;&gt;&lt;/S&gt;

So, I need either to change the XML element name somehow, or change the tag on the field. Is this possible?

I checked reflect (https://golang.org/pkg/reflect/#Value.FieldByName), but since FieldByName returns a value type and there are no Set methods, I don't think it's possible to use reflection.

答案1

得分: 2

你可以使用,attr,omitempty的组合方式来实现。

type S struct {
    Value    string `xml:"value,attr,omitempty"`
    Category string `xml:"category,attr,omitempty"`
}

或者你可以创建一个自定义的xml.MarshalerAttr

type S struct {
    Value Val `xml:",attr"`
}

type Val struct{ string }

func (v Val) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
    if v.string == "One" {
        return xml.Attr{Name: xml.Name{Local: "value"}, Value: v.string}, nil
    }

    return xml.Attr{Name: xml.Name{Local: "category"}, Value: v.string}, nil
}

第一种方法更简单但不够灵活,而且会使结构体变得更大。第二种方法稍微复杂一些,但更健壮和灵活。

英文:

(I don't know why did the other person delete their answer, but here's mine.)

You can either use a combination of ,attr and ,omitempty

type S struct {
	Value    string `xml:&quot;value,attr,omitempty&quot;`
	Category string `xml:&quot;category,attr,omitempty&quot;`
}

(playground: http://play.golang.org/p/C9tRlA80RV) or create a custom xml.MarshalerAttr

type S struct {
	Value Val `xml:&quot;,attr&quot;`
}

type Val struct{ string }

func (v Val) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
	if v.string == &quot;One&quot; {
		return xml.Attr{Name: xml.Name{Local: &quot;value&quot;}, Value: v.string}, nil
	}

	return xml.Attr{Name: xml.Name{Local: &quot;category&quot;}, Value: v.string}, nil
}

(playground: http://play.golang.org/p/a1Wd2gonb_).

The first approach is easier but less flexible, and also makes the struct bigger. The second approach is slightly more complex, but also more robust and flexible.

huangapple
  • 本文由 发表于 2015年6月19日 22:12:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/30940332.html
匿名

发表评论

匿名网友

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

确定