Golang中如何处理JSON解析出现的垃圾值?

Golang中如何处理JSON解析出现的垃圾值? 这是期望的结果:DB Fiddle - SQL 数据库游乐场

image

PGAdmin 也有类似的结果: image

但从 Go 程序获取时,它却用垃圾值替代了 JSON 值:

[{“status_id”:0,“val”:“IkFrdGl2Ig==”},{“status_id”:1,“val”:“IkluYWt0aXYi”}]

查询语句:

WITH list AS 
(SELECT status_id, json_array_elements(lang) as row FROM status)
SELECT status_id, row -> 'val' as val FROM list
WHERE row ->> 'key' = $1

Go 代码:

// query to return a list with status languages
func getlang(query string, val string) interface{} {
	if len(query) > 0 {
		var list []map[string]interface{}
		rows, err := db.Queryx(query, val)
		if err != nil {
			log("no records")
		}

		defer rows.Close()

		for rows.Next() {
			row := make(map[string]interface{})
			err = rows.MapScan(row)
			if err != nil {
				log(err.Error())
			}
			list = append(list, row)
		}

		rows.Close()
		if len(list) == 0 {
			return ("norec")
		}
		return list
	}
	return nil
}

查询在 Go 环境外运行正常,但在 Go 中却用垃圾值替换了实际值? 无论我是否使用占位符,结果都一样。 我到底做错了什么?


更多关于Golang中如何处理JSON解析出现的垃圾值?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

14 回复

我完全明白了你的意思。你让我理解起来容易多了。

更多关于Golang中如何处理JSON解析出现的垃圾值?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Sibert:

其他查询未使用JSON。

Sibert:

以及为什么只有一个JSON列

其他内容是否也未使用JSON,还是所有其他JSON都“正常”?

NobbZ:

没有其他东西在使用 JSON 吗?

这是我的第一个 JSON 列。其他列使用的是“普通”列。而且只有 JSON 列不是 UTF-8 编码。

你还没有展示任何其他查询,也没有提供相关表格和列的详细信息。

不过,从“PGAdmin”的截图来看,val 列似乎是一个 JSON 列。不确定其他“能工作”的东西是什么类型。

您使用哪种数据库驱动?它原生支持JSON列吗?为什么您要为纯字符串使用JSON列?

您如何将数据库查询结果转换为分享的JSON?如果使用 fmt.Printf("%#v", list) 直接检查查询结果,它看起来是什么样子?

后者在README中提到它处于维护模式,但其描述读起来更像是为了支持GitHub - jackc/pgx: PostgreSQL driver and toolkit for Go而进行的自我贬低,而后者在其README中明确提到了对JSON和JSONB的支持。

我找到了一个解决方案:

不使用 “JSON 对象” row -> 'val'(意为 “Active”

我使用了 “值” row ->> ''val''(意为 Active

问题依然存在。为什么 Go 不能像 dbfiddle 和 PGAdmin 那样处理带有 UTF-8 的对象?

这就是我问你使用哪个驱动以及它是否支持JSON列的原因。

从其他语言中我了解到,对JSON列的支持通常是不完整的。例如,Elixir的ecto只支持JSON列中的映射和数组,并且值必须是同构类型的。

// 代码示例(如有)

Sibert:

[{“status_id”:0,“val”:“IkFrdGl2Ig==”},{“status_id”:1,“val”:“IkluYWt0aXYi”}]

这不是“乱码”:

$ base64 --decode <<< IkFrdGl2Ig==
"Aktiv"                                                          
$ base64 --decode <<< IkluYWt0aXYi
"Inaktiv"

NobbZ:

$ base64 --decode <<< "IkFrdGl2Ig=="
"Aktiv"                                                          
$ base64 --decode <<< "IkluYWt0aXYi"
"Inaktiv"%

为什么我只需要解码“val”?而不是查询结果的其他部分?

还有,为什么其他所有查询都不需要解码,唯独这个需要?

NobbZ:

你还没有展示任何其他查询

其他查询未使用JSON。

你也没有展示有关相关表和列的任何详细信息。

image

但问题仍然存在:在哪里以及如何解码,以及为什么只有一个JSON列?所有其他列都是正确的UTF-8编码。

@Sibert@niamul21 关于驱动问题的问答是正确的答案,但如果你想在拥有 sqlx 结构体扫描功能的同时,又能获得原生级别的 pgx 支持,这里可能遗漏了一个很好的建议。

在这种情况下,使用 lib/pq 配合 sqlx 意味着你没有使用原生的 PostgreSQL 类型,并且所有内容都以文本形式传递给数据库。pgx 库支持所有 PostgreSQL 类型,性能可能更好,能获得安全更新(因为它不像你正在使用的仓库那样已归档),最后,如果你想要 sqlx 的功能(这是一个很好的库,但它只使用数据库接口,无法使用原生接口),那么你或许可以转向使用 scany:https://github.com/georgysavva/scany

NobbZ:

这就是我问你使用哪个驱动以及它是否支持JSON列的原因。

GitHub

GitHub - jmoiron/sqlx: general purpose extensions to golang’s database/sql

general purpose extensions to golang’s database/sql - GitHub - jmoiron/sqlx: general purpose extensions to golang’s database/sql

GitHub

GitHub - lib/pq: Pure Go Postgres driver for database/sql

Pure Go Postgres driver for database/sql. Contribute to lib/pq development by creating an account on GitHub.

我怎么知道这些驱动是否支持JSON?

问题出在数据库返回的JSON值被Base64编码了。从你展示的PGAdmin结果可以看出,val字段包含的是Base64编码的字符串,但在Go中直接解析时没有进行解码。

在你的例子中:

  • "IkFrdGl2Ig==" 解码后是 "Aktiv"
  • "IkluYWt0aXYi" 解码后是 "Inaktiv"

你需要对从数据库获取的val字段进行Base64解码。以下是修正后的代码:

import (
    "encoding/base64"
    "encoding/json"
    "database/sql"
    "github.com/jmoiron/sqlx"
)

func getlang(query string, val string) interface{} {
    if len(query) > 0 {
        type Row struct {
            StatusID int    `db:"status_id"`
            Val      string `db:"val"`
        }
        
        var rows []Row
        err := db.Select(&rows, query, val)
        if err != nil {
            log("no records or error: " + err.Error())
            return "norec"
        }
        
        if len(rows) == 0 {
            return "norec"
        }
        
        // 解码Base64并构建结果
        var result []map[string]interface{}
        for _, row := range rows {
            decodedBytes, err := base64.StdEncoding.DecodeString(row.Val)
            if err != nil {
                log("base64 decode error: " + err.Error())
                continue
            }
            
            // 去除可能的JSON引号
            decodedStr := string(decodedBytes)
            if len(decodedStr) >= 2 && decodedStr[0] == '"' && decodedStr[len(decodedStr)-1] == '"' {
                decodedStr = decodedStr[1 : len(decodedStr)-1]
            }
            
            result = append(result, map[string]interface{}{
                "status_id": row.StatusID,
                "val":       decodedStr,
            })
        }
        
        return result
    }
    return nil
}

或者,如果你需要保持原始的数据结构,但解码val字段:

func getlang(query string, val string) interface{} {
    if len(query) > 0 {
        var list []map[string]interface{}
        rows, err := db.Queryx(query, val)
        if err != nil {
            log("no records")
            return "norec"
        }
        
        defer rows.Close()
        
        for rows.Next() {
            row := make(map[string]interface{})
            err = rows.MapScan(row)
            if err != nil {
                log(err.Error())
                continue
            }
            
            // 解码Base64的val字段
            if valStr, ok := row["val"].(string); ok {
                decodedBytes, err := base64.StdEncoding.DecodeString(valStr)
                if err == nil {
                    decodedStr := string(decodedBytes)
                    // 去除JSON字符串的引号
                    if len(decodedStr) >= 2 && decodedStr[0] == '"' && decodedStr[len(decodedStr)-1] == '"' {
                        decodedStr = decodedStr[1 : len(decodedStr)-1]
                    }
                    row["val"] = decodedStr
                }
            }
            
            list = append(list, row)
        }
        
        if len(list) == 0 {
            return "norec"
        }
        return list
    }
    return nil
}

问题的根源是数据库中的JSON值被存储为Base64编码的字符串。"IkFrdGl2Ig==""Aktiv"的Base64编码,"IkluYWt0aXYi""Inaktiv"的Base64编码。Go代码需要显式解码这些值才能得到正确的字符串内容。

回到顶部