unmarshal generic json in Go

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

unmarshal generic json in Go

问题

我是一个新的Go程序员(来自Java),我想要在Go中复现一种通用的简单使用的方式。

我想要创建一些函数,允许我对JSON字符串进行解析,以避免代码重复。

这是我的当前代码,但它不起作用:

type myStruct1 struct {
    id   string
    name string
}

func (obj myStruct1) toString() string {
    var result bytes.Buffer
    result.WriteString("id : ")
    result.WriteString(obj.id)
    result.WriteString("\n")
    result.WriteString("name : ")
    result.WriteString(obj.name)

    return result.String()
}

func main() {
    content := `{id:"id1",name="myName"}`
    object := myStruct1{}
    parseJSON(content, object)

    fmt.Println(object.toString())
}

func parseJSON(content string, object interface{}) {
    var parsed interface{}
    json.Unmarshal([]byte(content), &parsed)
}

运行这段代码后,返回的结果是:

id : 
name : 

你有什么想法吗?

谢谢!

英文:

I'm a new Go programmer (From Java) and I would like to reproduce a generic way which is esay to use in Java.

I want to create some function which allow me to do an Unmarshal on a JSON string in order to avoid code duplicity.

This is my current code which is not working :

type myStruct1 struct {
    id string
    name string
}

func (obj myStruct1) toString() string {
    var result bytes.Buffer
    result.WriteString("id : ")
    result.WriteString(obj.id)
    result.WriteString("\n")
    result.WriteString("name : ")
    result.WriteString(obj.name)

    return result.String()
}

func main() {

    content := `{id:"id1",name="myName"}`
    object := myStruct1{}
    parseJSON(content, object)

    fmt.Println(object.toString()) 
}

func parseJSON(content string, object interface{}) {
    var parsed interface{}
    json.Unmarshal([]byte(content), &parsed)
}

This code, on run, returns me this :

id : 
name : 

Do you have any idea ?

Thanks

答案1

得分: 19

问题是你想要写入一个通用类型?你可能想要使用字符串映射。这在 BSON 中也适用:

var anyJson map[string]interface{}
json.Unmarshal(bytes, &anyJson)

你可以这样访问字段:

anyJson["id"].(string)

不要忘记对值进行类型断言,它们必须是正确的类型,否则会引发恐慌。(你可以在 golang 网站上阅读更多关于类型断言的内容)

英文:

The issue is you want to write to a generic type? You probably want a string map. This works with BSON anyways:

var anyJson map[string]interface{}
json.Unmarshal(bytes, &anyJson)

You'll be able to access the fields like so:

anyJson["id"].(string)

Don't forget to type assert your values, and they must be the correct type or they'll panic. (You can read more about type assertions on the golang site)

答案2

得分: 5

解析“通用 JSON”时,当你不知道它的模式时:

	var parsed any
	err := json.Unmarshal(jsonText, &parsed)

返回的parsed中的any将是map[string]any[]anynil或单个值float64boolstring

你可以测试类型并做出相应的反应。

import (
	"encoding/json"
	"fmt"
)

func test(jsonText []byte) {
	// 解析
	var parsed any
	err := json.Unmarshal(jsonText, &parsed)
	if err != nil {
		panic(err) // 输入格式错误
	}

	// 类型特定的逻辑
	switch val := parsed.(type) {
	case nil:
		fmt.Println("json 指定为 null")
	case map[string]any:
		fmt.Printf("id:%s name:%s\n", val["id"], val["name"])
	case []any:
		fmt.Printf("包含 %d 个项目的列表\n", len(val))
	case float64:
		fmt.Printf("单个数字 %f\n", val)
	case bool:
		fmt.Printf("单个布尔值 %v\n", val)
	case string:
		fmt.Printf("单个字符串 %s\n", val)
	default:
		panic(fmt.Errorf("意外的类型 %T", parsed))
	}
}
英文:

To parse "generic JSON" when you have no idea what schema it has:

	var parsed any
	err := json.Unmarshal(jsonText, &parsed)

The returned any in parsed will be a map[string]any or []any or nil or single values float64, bool, string.

You can test the type and react accordingly.

import (
	"encoding/json"
	"fmt"
)

func test(jsonText []byte) {
	// parsing
	var parsed any
 	err := json.Unmarshal(jsonText, &parsed)
	if err != nil {
		panic(err) // malformed input
	}

	// type-specific logic
	switch val := parsed.(type) {
	case nil:
		fmt.Println("json specifies null")
	case map[string]any:
		fmt.Printf("id:%s name:%s\n", val["id"], val["name"])
	case []any:
		fmt.Printf("list of %d items\n", len(val))
	case float64:
		fmt.Printf("single number %f\n", val)
	case bool:
		fmt.Printf("single bool %v\n", val)
	case string:
		fmt.Printf("single string %s\n", val)
	default:
		panic(fmt.Errorf("type %T unexpected", parsed))
	}
}

答案3

得分: 1

Unmarshal只会设置结构体中的公开字段。

这意味着你需要修改JSON结构体,使用大写字母开头的字段名:

type myStruct1 struct {
    Id string
    Name string
}

这样做的原因是,JSON库在没有导出字段的情况下,无法使用反射查看字段。

英文:

Unmarshal will only set exported fields of the struct.

Which means you need to modify the json struct to use capital case letters:

type myStruct1 struct {
    Id string
    Name string
}

The reason behind this is that the json library does not have the ability to view fields using reflect unless they are exported.

答案4

得分: 0

你必须导出你的字段:

type myStruct1 struct {
    Id string
    Name string
}

请参阅文档中的导出标识符

英文:

You have to export your fields:

type myStruct1 struct {
    Id string
    Name string
}

See Exported Identifiers from documentation.

答案5

得分: 0

你需要在代码中进行一些更改才能使其正常工作:

  1. 函数json.Unmarshal只能设置结构体中公开的变量,也就是以大写字母开头的变量。在myStruct1内部,使用类似IDName的变量名。
  2. 你的内容不是有效的JSON。你实际上想要的是{"ID":"id1","Name":"myName"}
  3. 你将object传递给了parseJSON,但你在解析字符串时使用的是parsed而不是object。让parseJSON接收一个*myStruct(而不是interface{}),并在解组字符串时使用该变量而不是parsed。此外,始终处理错误返回,例如err := json.Unmarshal(content, object),并检查err

我建议你参考Golang之旅进行学习。

英文:

There are a few changes you need to make in your code to make it work:

  1. The function json.Unmarshal can only set variables inside your struct which are exported, that is, which start with capital letters. Use something like ID and Name for your variable names inside myStruct1.
  2. Your content is invalid JSON. What you actually want is {"ID":"id1","Name":"myName"}.
  3. You're passing object to parseJSON but you're using parsed instead, not object. Make parseJSON receive a *myStruct (instead of an interface{}), and use that variable instead of parsed when unmarshalling the string. Also, always handle the error returns, like err := json.Unmarshal(content, object), and check err.

I'd suggest you to do the Golang Tour unmarshal generic json in Go

答案6

得分: -1

你还可以将文件设置为另一个结构体内部具有动态属性的对象。这样可以添加元数据,并以相同的方式读取它。

type MyFile struct {
    Version string
    Data    map[string]interface{}
}
英文:

You can also set the file as an Object with dynamic properties inside another struct. This will let you add metadata and you read it the same way.

type MyFile struct {
	Version string
	Data  map[string]interface{}
}

huangapple
  • 本文由 发表于 2016年3月17日 21:31:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/36062052.html
匿名

发表评论

匿名网友

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

确定