Go:无法解组 JSON 值

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

Go: Unable to unmarshal json value

问题

我有一个存储在PostgreSQL列中的jsonb值,我需要将其转换为object以发送响应。

我无法使用json.Unmarshal方法将JSON转换为结构体。

type Result struct {
	Id         int
	Expertise  string // 因为是JSON字符串
	Languages  string // 因为是JSON字符串
}

// 将查询数据保存到result结构体中
e, _ := json.Marshal(data.Expertise)
l, _ := json.Marshal(data.Languages)
row := r.db.QueryRow(`
	INSERT INTO filters (user_id, expertise, languages)
	VALUES ($1, $2, $3)
	ON CONFLICT (user_id) DO 
	UPDATE SET expertise=$2, languages=$3
	RETURNING id, expertise, languages;
`, userid, e, l)

var res Result
err := row.Scan(&res.Id, &res.Expertise, &res.Languages)

然后我取出Expertise和Languages字段并对它们进行解码

// 辅助方法
func Unmarshal(value string) interface{} {
	var obj interface{}
	err := json.Unmarshal([]byte(value), &obj)
	return obj
}

type CreateFilterResponse struct {
	Id         int         `json:"id"`
	Expertise  interface{} `json:"expertise"`
	Languages  interface{} `json:"languages"`
}

response := dto.CreateFilterResponse{
	Id:         res.Id,
	Expertise:  Unmarshal(res.Expertise), // 未解码,仍然是JSON
	Languages:  Unmarshal(res.Languages), // 未解码,仍然是JSON
}

我真的很感谢帮助,我希望ExpertiseLanguages分别是{}[]

这是我得到的响应:

{"id":5,"expertise":"[{\"role\":1,\"experience\":5},{\"role\":3,\"experience\":4}]","languages":"[1,2,3]"}

注意:

  • Expertise是jsonb列:[]{role: int, experience: int}
  • Languages是jsonb列:[]int
英文:

I have jsonb value stored in postgres columns which I need to convert to object to send the response

I am unable to convert JSON -> struct using json.Unmarshal method

type Result struct {
	Id 			int 
	Expertise 	string // because json string 
	Languages 	string // because json string 
}

// saving dd query data to result struct 
e, _ := json.Marshal(data.Expertise)
l, _ := json.Marshal(data.Languages)
	row := r.db.QueryRow(`
		INSERT INTO filters (user_id, expertise, languages)
		VALUES ($1, $2, $3)
		ON CONFLICT (user_id) DO 
		UPDATE SET expertise=$2, languages=$3
		RETURNING id, expertise, languages;
	`, userid, e, l)

	var res Result 
	err := row.Scan(&res.Id, &res.Expertise, &res.Languages)

Then I take this the Expertise and Languages field and UNMARSHAL THEM

// helper method 
func Unmarshal(value string) interface{} {
	var obj interface{}
	err := json.Unmarshal([]byte(value), &obj)
	return obj
}

type CreateFilterResponse struct {
	Id 		  int 			`json:"id"`
	Expertise interface{} 	`json:"expertise"`
	Languages interface{} 	`json:"languages"`
}

response := dto.CreateFilterResponse{
		Id: res.Id,
		Expertise: Unmarshal(res.Expertise), // 👈 not decoding, still json
		Languages: Unmarshal(res.Languages), // 👈 not decoding, still json
}

I would really appreciate the help, I need the Expertise and Languages to be {} and [] respectively

This is what I am getting in response:

{"id":5,"expertise":"[{\"role\":1,\"experience\":5},
{\"role\":3,\"experience\":4}]","languages":"[1,2,3]"}

<h3>Note: </h3>

  • Expertise is jsonb column : []{role: int, experience: int}

  • Languages is jsonb column : []int

答案1

得分: 1

感谢@Blackgreen的评论,建议起作用了。

正如他建议的:“我认为问题在于你将jsonb字节扫描为字符串,这导致它被字面解释。然后,当你将字符串传递给json.Unmarshal时,它仍然是一个字符串。将Expertise和Language的类型更改为[]byte(或json.RawMessage)。”

我也做了同样的事情,它起作用了,这是代码:

// 用于解析来自数据库查询的jsonb
type Result struct {
	Id 			int 
	Expertise 	json.RawMessage // 更改
	Languages 	json.RawMessage // 更改 
}

// 数据库查询本身
e, _ := json.Marshal(filters.Expertise)
l, _ := json.Marshal(filters.Languages)

row := r.db.QueryRow(`
		INSERT INTO filters (user_id, expertise, languages)
		VALUES ($1, $2, $3)
		ON CONFLICT (user_id) DO 
		UPDATE SET expertise=$2, languages=$3
		RETURNING id, expertise, languages;
	`, userid, e, l)

var res Result 
err := row.Scan(&res.Id, &res.Expertise, &res.Languages) // 扫描到结果
if err != nil {
		fmt.Println("无法创建新的过滤器==>", err)
		return nil, err
}

然后,我使用一个辅助方法对expertiselanguages的jsonb值进行了Unmarshal,并为客户端创建了一个Response结构:

// 响应结构类型
type CreateFilterResponse struct {
	Id 			int			`json:"id"`
	Expertise 	interface{}	`json:"expertise"`
	Languages 	interface{}	`json:"languages"`
}

// 最终响应结构
response := dto.CreateFilterResponse{
    Id: res.Id,
    Expertise: Unmarshal(res.Expertise),
	Languages: Unmarshal(res.Languages),
}	

// 辅助方法
func Unmarshal(value []byte) interface{} {
	var obj interface{}
	json.Unmarshal(value, &obj)
	return obj
}

目前这样可以工作了。我仍然需要找到一种简单的方法来做到这一点,因为这是为了执行一个简单的任务而需要太多样板代码。 就像在JS/TS中可以使用一行代码JSON.parse(value)来完成

英文:

Thanks to @Blackgreen comment, the suggestion worked.

As he suggested "I believe the issue is that you are scanning the jsonb bytes into string, which causes it to be interpreted literally. Then when you pass a string into json.Unmarshal it stays a string. Change the type of Expertise and Language to []byte (or json.RawMessage)"

I did the same and it works, here is the code:

// for parsing jsonb coming from db query
type Result struct {
	Id 			int 
	Expertise 	json.RawMessage // change
	Languages 	json.RawMessage // change 
}

// the database query itself 
e, _ := json.Marshal(filters.Expertise)
l, _ := json.Marshal(filters.Languages)

row := r.db.QueryRow(`
		INSERT INTO filters (user_id, expertise, languages)
		VALUES ($1, $2, $3)
		ON CONFLICT (user_id) DO 
		UPDATE SET expertise=$2, languages=$3
		RETURNING id, expertise, languages;
	`, userid, e, l)

var res Result 
err := row.Scan(&amp;res.Id, &amp;res.Expertise, &amp;res.Languages) // scanning to result
if err != nil {
		fmt.Println(&quot;Unable to create a new filter ==&gt;&quot;, err)
		return nil, err
}

Then I Unmarshalled the expertise and languages jsonb values using a helper method and created a Response struct for the client:

// response struct type 
type CreateFilterResponse struct {
	Id 			int			`json:&quot;id&quot;`
	Expertise 	interface{}	`json:&quot;expertise&quot;`
	Languages 	interface{}	`json:&quot;languages&quot;`
}

// final response struct 
response := dto.CreateFilterResponse{
    Id: res.Id,
    Expertise: Unmarshal(res.Expertise),
	Languages: Unmarshal(res.Languages),
}	

// helper method 
func Unmarshal(value []byte) interface{} {
	var obj interface{}
	json.Unmarshal(value, &amp;obj)
	return obj
}

This works for now. I still need to find an easy way to do this as this is too much boilerplate code to perform a simple task. like in JS/TS it can be done using single line of code JSON.parse(value)

答案2

得分: -1

问题出在 JSON 输出中。它对你的 JSON 进行了两次编码,使得阅读和反序列化 JSON 变得困难。

你可以按照以下方式组织你的结构:

type Expertise struct {
	Role       int `json:"role"`
	Experience int `json:"Experience"`
}

type CreateFilterResponse struct {
	Id        int         `json:"id"`
	Expert    []Expertise `json:"expertise"`
	Languages []int       `json:"languages"`
}

我创建了一个更简洁的方法来示范和方便理解。它将删除不必要的引号和转义字符,这样你就可以将你的字符串转换为 []byte,并解组成你的结构。

func jsonCleaner(quoted string) []byte{
	quoted = strings.ReplaceAll(quoted, "\n", "")
	quoted = strings.ReplaceAll(quoted, "\\\"", "\"")
	quoted = strings.ReplaceAll(quoted, "]\"", "]")
	quoted = strings.ReplaceAll(quoted, "\"[", "[")
	dataBytes := []byte(quoted)
	fmt.Println(dataBytes)
	return dataBytes
} 

你的 JSON 消息将从:

{"id":5,"expertise":"[{\"role\":1,\"experience\":5},{\"role\":3,\"experience\":4}]","languages":"[1,2,3]"}

变为:

{"id":5,"expertise":[{"role":1,"experience":5},{"role":3,"experience":4}],"languages":[1,2,3]}
英文:

The problem is in the json output. It is encoding your JSON twice, making it difficult to read and deserialize the json in your structure.

You can start by organizing your structures as follows:

type Expertise struct {
	Role       int `json:&quot;role&quot;`
	Experience int `json:&quot;Experience&quot;`
}

type CreateFilterResponse struct {
	Id        int         `json:&quot;id&quot;`
	Expert    []Expertise `json:&quot;expertise&quot;`
	Languages []int       `json:&quot;languages&quot;`
}

I made a cleaner method just to exemplify and facilitate understanding. It will remove the unnecessary quotes and escapes so you can convert your string to []bytes and unmarshal your structure.

func jsonCleaner(quoted string) []byte{
	quoted = strings.ReplaceAll(quoted, &quot;\n&quot;, &quot;&quot;)
	quoted = strings.ReplaceAll(quoted, &quot;\\&quot;, &quot;&quot;)
	quoted = strings.ReplaceAll(quoted, &quot;]\&quot;&quot;, &quot;]&quot;)
	quoted = strings.ReplaceAll(quoted, &quot;\&quot;[&quot;, &quot;[&quot;)
	dataBytes := []byte(quoted)
	fmt.Println(dataBytes)
	return dataBytes
} 

Your json message would go from:

{&quot;id&quot;:5,&quot;expertise&quot;:&quot;[{\&quot;role\&quot;:1,\&quot;experience\&quot;:5},{\&quot;role\&quot;:3,\&quot;experience\&quot;:4}]&quot;,&quot;languages&quot;:&quot;[1,2,3]&quot;}

To:

{&quot;id&quot;:5,&quot;expertise&quot;:[{&quot;role&quot;:1,&quot;experience&quot;:5},&quot;role&quot;:3,&quot;experience&quot;:4}],&quot;languages&quot;:[1,2,3]}

huangapple
  • 本文由 发表于 2022年1月11日 04:26:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/70658523.html
匿名

发表评论

匿名网友

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

确定