如何将未解组的 Golang 对象转换为指定变量的类型

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

How to convert an unmarshalled Golang object to the type of a specified variable

问题

我将为您翻译以下内容:

我想将各种对象编组到文件中,然后解组它们,并通过获取编组的变量的类型将它们转换回其原始类型。
关键点是,我想将解组的对象转换为指定变量的类型,而不指定类型。

简短的伪代码:

// 编组
item := Book{"The Myth of Sisyphus", "Albert Camus"}
// 然后解组并转换为item变量的类型。
itemType := reflect.TypeOf(item)
newItem itemType = unmarshalledItem.(itemType) // 这是问题所在。
fmt.Println("解组的对象是:", reflect.TypeOf(newItem)) // 应该打印 *main.Book

完整的代码:

package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"reflect"
)

type Book struct {
Title string
Author string
}

func main() {
// 创建要编组的对象。
book := Book{"The Myth of Sisyphus", "Albert Camus"}
box := make(map[string]interface{})
box["The Myth of Sisyphus"] = &book
itemType := reflect.TypeOf(box["The Myth of Sisyphus"])
fmt.Println("Book的类型是:", itemType)

// 将对象编组到文件。
err := Write(&book)
if err != nil {
	fmt.Println("无法保存存储。", err)
	return
}

// 从文件解组对象。
untyped := make(map[string]interface{})
bytes, err := ioutil.ReadFile("store.txt")
if err != nil {
	fmt.Println("无法加载存储。", err)
	return
}
err = json.Unmarshal(bytes, &untyped)
if err != nil {
	fmt.Println("存储解组错误。", err)
	return
}

// 获取解组对象的Title属性,
// 并使用它来从box映射中获取变量类型。
for k, v := range untyped {
	if k == "Title" {
		itemTitle := v.(string)
		fmt.Println("转换具有标题的对象:", itemTitle)
		targetType := reflect.TypeOf(box[itemTitle])
		fmt.Println("要转换的类型是:", targetType)
		// 将untyped转换为targetType。
		// 这是问题所在。
		typed targetType = untyped.(targetType)
		fmt.Println("解组的对象是:", reflect.TypeOf(typed)) // 应该打印 *main.Book
	}
}

}

func Write(b *Book) error {
data, err := json.Marshal(b)
if err != nil {
return err
}
newFilename := "store.txt"
f, err := os.OpenFile(newFilename, os.O_CREATE|os.O_TRUNC, 0660)
if err != nil {
return err
}
_, err = f.WriteString(string(data) + "\n")
if err != nil {
return err
}
return nil
}

英文:

I'd like to marshal a variety of objects to file, then unmarshal them, and convert them back to their original type by getting the type of the variables which were marshalled.
The key point is that I'd like to convert the unmarshalled object to the type of a specified variable, without specifying the type.

The short pseudocode:

// Marshal this
item := Book{"The Myth of Sisyphus", "Albert Camus"}
// Then unmarshal and convert to the type of the item variable.
itemType := reflect.TypeOf(item)
newItem itemType = unmarshalledItem.(itemType)  // This is the problem.
fmt.Println("Unmarshalled is:", reflect.TypeOf(newItem)) // Should print *main.Book

The full code:

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
)

type Book struct {
	Title  string
	Author string
}

func main() {
	// Create objects to marshal.
	book := Book{"The Myth of Sisyphus", "Albert Camus"}
	box := make(map[string]interface{})
	box["The Myth of Sisyphus"] = &book
	itemType := reflect.TypeOf(box["The Myth of Sisyphus"])
	fmt.Println("Book is:", itemType)

	// Marshal objects to file.
	err := Write(&book)
	if err != nil {
		fmt.Println("Unable to save store.", err)
		return
	}

	// Unmarshal objects from file.
	untyped := make(map[string]interface{})
	bytes, err := ioutil.ReadFile("store.txt")
	if err != nil {
		fmt.Println("Unable to load store.", err)
		return
	}
	err = json.Unmarshal(bytes, &untyped)
	if err != nil {
		fmt.Println("Err in store unmarshal.", err)
		return
	}

	// Get Title property of unmarshalled object,
	// and use that to get variable type from box map.
	for k, v := range untyped {
		if k == "Title" {
			itemTitle := v.(string)
			fmt.Println("Cast item having title:", itemTitle)
			targetType := reflect.TypeOf(box[itemTitle])
			fmt.Println("Type to cast to is:", targetType)
			// Convert untyped to targetType.
			// This is the problem.
			typed targetType = untyped.(targetType)
			fmt.Println("Unmarshalled is:", reflect.TypeOf(typed)) // Should print *main.Book
		}
	}
}

func Write(b *Book) error {
	data, err := json.Marshal(b)
	if err != nil {
		return err
	}
	newFilename := "store.txt"
	f, err := os.OpenFile(newFilename, os.O_CREATE|os.O_TRUNC, 0660)
	if err != nil {
		return err
	}
	_, err = f.WriteString(string(data) + "\n")
	if err != nil {
		return err
	}
	return nil
}

答案1

得分: 1

这在动态类型语言中可能有效,但在这里不起作用,因为Go是静态类型的。

该书并不是以Book的形式存储的,而是以JSON字符串的形式存储的,除非告诉JSON解组器它是一个Book,否则它不知道它是一个Book。也就是说,它不知道如何将字段映射到Book对象。

你不能将解组后的“非类型化”对象强制转换为Book,因为它不是一个Book,而是一个map[string]interface{},只是看起来与Book完全相同。

你需要做的是:

  1. 将JSON解组为map[string]interface{},然后读取标题。
  2. 根据类型使用if语句或switch语句。
  3. 将JSON再次解组为该类型的对象(例如Book)。

大概是这样的:

// 检查类型
if targetType.String() == "*main.Book" {
// 再次解组为Book
var typedBook Book
_ = json.Unmarshal(bytes, &typedBook)
fmt.Println("解组后的类型是:", reflect.TypeOf(typedBook)) // 应该打印 *main.Book
} else if targetType.String() == "*main.Magazine" {
// 解组为杂志或其他类型
}

英文:

This might work in a dynamically typed language, but it wont work here because Go is statically typed.

The book is not stored as a Book, its stored as a json string and the json unmarshaller has no idea that its a Book unless you tell it so. i.e. it doesn't know to map the fields to a Book object.

You can't cast the unmarshalled 'untyped' into a Book because it is not a Book it is a map[string]interface{} which happens to look exactly like a Book.

What you would need to do is

  1. unmarshal the json into a map[string]interface{} then read the title.
  2. use an if statement or a switch statement based on the type
  3. unmarshal the json again into an object of that type (e.g. a Book)

Something like this I guess:

// check the type
if targetType.String() == "*main.Book" {
    // unmarshall it again as a Book
    var typedBook Book
    _ = json.Unmarshal(bytes, &typedBook)
    fmt.Println("Unmarshalled is:", reflect.TypeOf(typedBook)) // Should print *main.Book
} else if targetType.String() == "*main.Magazine" {
    // unmarshal a magazine or whatever
} 

huangapple
  • 本文由 发表于 2014年10月26日 05:06:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/26567249.html
匿名

发表评论

匿名网友

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

确定