使用Go语言进行编组/解组时,通过仅使用一种结构类型来重新组织XML。

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

Restructuring xml by using only one struct type for marshal/unmarshal-ing in go

问题

假设你想要使用原始结构体"v"自动进行编组,并使用不同的XML标签,而不是像现在这样分配一个新的结构体类型"newV"并逐个复制字段。这是可能的吗?

不幸的是,Go语言的encoding/xml包不支持直接在编组时更改XML标签。在这种情况下,你需要使用两个不同的结构体类型来表示不同的XML结构,并进行字段复制。

你可以尝试使用反射来自动复制字段,但这可能会导致性能下降,并且代码会更加复杂。因此,目前最简单和可读性最好的方法是创建一个新的结构体类型并手动复制字段。

如果你对性能要求不高,可以尝试使用反射来自动复制字段。以下是一个示例代码:

package main

import (
	"encoding/xml"
	"fmt"
	"os"
	"reflect"
)

type Result struct {
	Person string `xml:"person>name"`
	Id     int    `xml:"id>number"`
}

type Restructured struct {
	Person string
	Id     int
}

const data = `
<Result>
	<person>
		<name>Dave</name>
	</person>
	<id>
		<number>1234</number>
	</id>
</Result>`

func main() {
	v := Result{}
	err := xml.Unmarshal([]byte(data), &v)
	if err != nil {
		fmt.Printf("error: %v", err)
		return
	}

	newV := Restructured{}
	copyFields(&newV, &v)

	output, err := xml.MarshalIndent(newV, "  ", "    ")
	if err != nil {
		fmt.Printf("error: %v\n", err)
	}

	os.Stdout.Write(output)
}

func copyFields(dest, src interface{}) {
	destValue := reflect.ValueOf(dest).Elem()
	srcValue := reflect.ValueOf(src).Elem()

	for i := 0; i < destValue.NumField(); i++ {
		destField := destValue.Field(i)
		srcField := srcValue.FieldByName(destValue.Type().Field(i).Name)

		if destField.CanSet() && srcField.IsValid() {
			destField.Set(srcField)
		}
	}
}

请注意,使用反射可能会导致性能下降,并且代码会更加复杂。因此,如果性能是一个关键问题,手动复制字段可能是更好的选择。

英文:

Say I have following block of xml data.

  &lt;Result&gt;
&lt;person&gt;
&lt;name&gt;Dave&lt;/name&gt;
&lt;/person&gt;
&lt;id&gt;
&lt;number&gt;1234&lt;/number&gt;
&lt;/id&gt;
&lt;/Result&gt;

and I want to restructure it to following.

  &lt;Restructured&gt;
&lt;Person&gt;Dave&lt;/Person&gt;
&lt;Id&gt;1234&lt;/Id&gt;
&lt;/Restructured&gt;

But only using one struct to do it. Is it possible?

Attaching code below.

package main
import (
&quot;encoding/xml&quot;
&quot;fmt&quot;
&quot;os&quot;
)
type Result struct {
Person string `xml:&quot;person&gt;name&quot;`
Id     int    `xml:&quot;id&gt;number&quot;`
}
type Restructured struct {
Person string
Id     int
}
const data = `
&lt;Result&gt;
&lt;person&gt;
&lt;name&gt;Dave&lt;/name&gt;
&lt;/person&gt;
&lt;id&gt;
&lt;number&gt;1234&lt;/number&gt;
&lt;/id&gt;
&lt;/Result&gt;`
func main() {
v := Result{}
err := xml.Unmarshal([]byte(data), &amp;v)
if err != nil {
fmt.Printf(&quot;error: %v&quot;, err)
return
}
newV := Restructured{Person: v.Person, Id: v.Id}
output, err := xml.MarshalIndent(newV, &quot;  &quot;, &quot;    &quot;)
if err != nil {
fmt.Printf(&quot;error: %v\n&quot;, err)
}
os.Stdout.Write(output)
}

I don't want to do it like this, allocating a new type of struct("newV") and copying field by field.
Is there any way to marshal automatically with different xml tags using original struct "v"?

答案1

得分: 2

使用第二个结构体是我看到的最方便的方法。如果你不想将其导出,实际上可以在MarshalXML中隐藏另一个类型:

func (r *Result) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
    type Restructured struct {
        Person string
        ID     int `xml:"Id"`
    }
    rr := &Restructured{Person: r.Person, ID: r.ID}
    return enc.Encode(rr)
}

Playground: http://play.golang.org/p/jZU2Jg0ZRZ.

英文:

Using a second struct is the most convenient way I see. If you don't want to make it exported, you can actually hide the other type inside your MarshalXML:

func (r *Result) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
type Restructured struct {
Person string
ID     int `xml:&quot;Id&quot;`
}
rr := &amp;Restructured{Person: r.Person, ID: r.ID}
return enc.Encode(rr)
}

Playground: http://play.golang.org/p/jZU2Jg0ZRZ.

huangapple
  • 本文由 发表于 2015年11月24日 23:11:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/33897193.html
匿名

发表评论

匿名网友

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

确定