将JSON解组为反射结构体。

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

Unmarshal json to reflected struct

问题

可以使用反射将JSON解组成一个由反射创建的结构体,而无需硬编码原始类型。在这个例子中,你可以使用以下代码:

package main

import (
	"fmt"
	"encoding/json"
	"reflect"
)

type Employee struct {
	Firstname string     `json:"firstname"`
}

func main() {
	// 原始结构体
	orig := new(Employee)

	t := reflect.TypeOf(orig)
	v := reflect.New(t.Elem())

	// 反射结构体
	new := v.Elem().Interface()

	// 解组到反射结构体
	json.Unmarshal([]byte(`{"firstname": "bender"}`), &new)

	fmt.Printf("%+v\n", new)
}

在这个例子中,我们使用了类型断言将new转换为Employee类型。但是,如果你不知道类型,可以直接使用v进行解组,但是结构体将会被置零:

json.Unmarshal([]byte(`{"firstname": "bender"}`), v)

如果省略类型断言,你将得到一个映射(map),这是可以理解的:

json.Unmarshal([]byte(`{"firstname": "bender"}`), v.Elem().Interface())

希望这可以帮助到你!

英文:

Is it possible to unmarshal JSON into a struct made from reflection without hardcoding the original type?

<!-- language: golang -->

package main

import (
  &quot;fmt&quot;
  &quot;encoding/json&quot;
  &quot;reflect&quot;
)

type Employee struct {
  Firstname string     `json:&quot;firstname&quot;`
}

func main() {
  //Original struct
  orig := new(Employee)

  t := reflect.TypeOf(orig)
  v := reflect.New(t.Elem())

  //Reflected struct
  new := v.Elem().Interface().(Employee)

  // Unmarshal to reflected struct
  json.Unmarshal([]byte(&quot;{\&quot;firstname\&quot;: \&quot;bender\&quot;}&quot;), &amp;new)

  fmt.Printf(&quot;%+v\n&quot;, new)
}

I used a cast to Employee in this example. But what if i don't know the type?

When i just use v for the unmarhaling the struct will be zeroed.

json.Unmarshal([]byte(&quot;{\&quot;firstname\&quot;: \&quot;bender\&quot;}&quot;), v)

When I omit the cast I get a map. which is understandable

json.Unmarshal([]byte(&quot;{\&quot;firstname\&quot;: \&quot;bender\&quot;}&quot;), v.Elem().Interface())

答案1

得分: 17

问题在于,如果你在这里省略类型断言:

new := v.Elem().Interface()

new 的类型会被推断为 interface{}

然后,当你取地址进行解组时,&new 的类型是 *interface{}(指向接口的指针),而解组的结果并不符合你的期望。

如果你直接使用指针引用,可以避免类型断言。

func main() {
  // 原始结构体
  orig := new(Employee)

  t := reflect.TypeOf(orig)
  v := reflect.New(t.Elem())

  // 反射指针
  newP := v.Interface()

  // 解组到反射的结构体指针
  json.Unmarshal([]byte("{\"firstname\": \"bender\"}"), newP)

  fmt.Printf("%+v\n", newP)
}

Playground: https://play.golang.org/p/lTBU-1PqM4

英文:

The problem here is that if you omit the type assertion here:

new := v.Elem().Interface()

The new is inferred to have a interface{} type.

Then when you take the address to unmarshal, the type of &amp;new is *interface{} (pointer to interface{}) and unmarshal does not work as you expect.

You can avoid the type assertion if instead of getting the Elem() you work directly with the pointer reference.

func main() {
  //Original struct
  orig := new(Employee)

  t := reflect.TypeOf(orig)
  v := reflect.New(t.Elem())

  // reflected pointer
  newP := v.Interface()

  // Unmarshal to reflected struct pointer
  json.Unmarshal([]byte(&quot;{\&quot;firstname\&quot;: \&quot;bender\&quot;}&quot;), newP)

  fmt.Printf(&quot;%+v\n&quot;, newP)
}

Playground: https://play.golang.org/p/lTBU-1PqM4

答案2

得分: 3

// https://github.com/xiaojun207/go-base-utils/blob/master/utils/Clone.go
func NewInterface(typ reflect.Type, data []byte) interface{} {
	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		dst := reflect.New(typ).Elem()
		json.Unmarshal(data, dst.Addr().Interface())
		return dst.Addr().Interface()
	}else {
		dst := reflect.New(typ).Elem()
		json.Unmarshal(data, dst.Addr().Interface())
		return dst.Interface()
	}
}

type Employee struct {
	Firstname string     `json:"firstname"`
}

func main() {
	data := "{\"firstName\": \"John\"}"
	obj := NewInterface(reflect.TypeOf(Employee{}), []byte(data))
	fmt.Println("Employee:", obj)
}

这是一段Go语言代码,它定义了一个名为NewInterface的函数和一个名为Employee的结构体。NewInterface函数根据给定的类型和数据创建一个新的接口对象。如果类型是指针类型,它会创建一个指向该类型的指针,并将数据解析为该类型的对象。如果类型不是指针类型,它会创建一个该类型的对象,并将数据解析为该对象。Employee结构体定义了一个名为Firstname的字符串字段。

main函数中,它使用NewInterface函数创建了一个Employee对象,并将数据{"firstName": "John"}解析为该对象。然后,它打印输出了该对象。

请注意,代码中的json:"firstname"是用于指定结构体字段在JSON序列化和反序列化时的名称的标签。

英文:

// https://github.com/xiaojun207/go-base-utils/blob/master/utils/Clone.go
func NewInterface(typ reflect.Type, data []byte) interface{} {
	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		dst := reflect.New(typ).Elem()
		json.Unmarshal(data, dst.Addr().Interface())
		return dst.Addr().Interface()
	}else {
		dst := reflect.New(typ).Elem()
		json.Unmarshal(data, dst.Addr().Interface())
		return dst.Interface()
	}
}

type Employee struct {
	Firstname string     `json:&quot;firstname&quot;`
}

func main() {
	data := &quot;{\&quot;firstName\&quot;: \&quot;John\&quot;}&quot;
	obj := NewInterface(reflect.TypeOf(Employee{}), []byte(data))
	fmt.Println(&quot;Employee:&quot;, obj)
}

答案3

得分: 0

如果您完全不知道类型,可以将JSON字符串解组为interface{}。如果您需要处理解组后的数据,可以将其转换为所需的类型。

这里有一个示例:

package main

import (
	"encoding/json"
	"fmt"
	"reflect"
	"unsafe"
)

type Employee struct {
	Firstname string `json:"firstName"`
}

func deserialize(jsonData string) interface{} {
	var obj interface{}

	if err := json.Unmarshal([]byte(jsonData), &obj); err != nil {
		panic(err)
	}

	return obj
}

func NewEmployee(objData map[string]interface{}) *Employee {
	s := (*Employee)(nil)
	t := reflect.TypeOf(s).Elem()
	employeePtr := reflect.New(t)
	employee := (*Employee)(unsafe.Pointer(employeePtr.Pointer()))
	employee.Firstname = objData["firstName"].(string)

	return employee
}

func main() {
	jsonData := `{"firstName": "John"}`

	obj := deserialize(jsonData)

	objData := obj.(map[string]interface{})
	employee := NewEmployee(objData)

	fmt.Printf("%s\n", employee.Firstname)
}

您可以在Go Playground上进行测试。

英文:

If you don't know the type at all, you can Unmarshal the JSON string into an interface{}. If you then need to work with the Unmarshaled data, you can convert it to the desired type.

Here's an example:

package main

import (
	&quot;encoding/json&quot;
	&quot;fmt&quot;
	&quot;reflect&quot;
	&quot;unsafe&quot;
)

type Employee struct {
	Firstname string `json:&quot;firstName&quot;`
}

func deserialize(jsonData string) interface{} {
	var obj interface{}

	if err := json.Unmarshal([]byte(jsonData), &amp;obj); err != nil {
		panic(err)
	}

	return obj
}

func NewEmployee(objData map[string]interface{}) *Employee {
	s := (*Employee)(nil)
	t := reflect.TypeOf(s).Elem()
	employeePtr := reflect.New(t)
	employee := (*Employee)(unsafe.Pointer(employeePtr.Pointer()))
	employee.Firstname = objData[&quot;firstName&quot;].(string)

	return employee
}

func main() {
	jsonData := &quot;{\&quot;firstName\&quot;: \&quot;John\&quot;}&quot;

	obj := deserialize(jsonData)

	objData := obj.(map[string]interface{})
	employee := NewEmployee(objData)

	fmt.Printf(&quot;%s\n&quot;, employee.Firstname)
}

You can check it on the Go Playground.

huangapple
  • 本文由 发表于 2017年8月15日 00:57:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/45679408.html
匿名

发表评论

匿名网友

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

确定