sqlx库返回的结果看起来像是奇怪的Base64编码。

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

The sqlx library gives weird base64 encoded looking results

问题

我正在使用来自http://jmoiron.github.io/sqlx/的库进行查询。按照文档的指导,我得到了以下代码。

func cities(w http.ResponseWriter, r *http.Request, _ httprouter.Params) error {
    var main string
    var secondary string
    var limit string
    queryParams := make(map[string]interface{})

    if k := r.PostFormValue("keyword"); k != "" {
        main = "city.name LIKE :keyword"
        queryParams["keyword"] = k + "%"
    }

    if sk := r.PostFormValue("secondaryKeyword"); sk != "" && sk != "null" {
        secondary = "OR city.name = :secondaryKeyword"
        queryParams["secondaryKeyword"] = sk
    }

    if mr := r.PostFormValue("maxResults"); mr != "" {
        limit = "LIMIT :maxResults"
        queryParams["maxResults"] = mr
    }

    if lr := r.PostFormValue("lastRequest"); lr != "" && lr == "1" {
        limit = ""
    }

    query := fmt.Sprintf(`
        SELECT
            city.geonameid AS cityId,
            city.name AS cityName,
            COALESCE(admin1.name_local, '') AS admin1Name,
            country.name AS countryName,
            CONCAT_WS(' ', city.name, city.asciiname, country.name) AS searchString
        FROM geonames_cities1000 AS city
            INNER JOIN geonames_countryinfo AS country
                ON city.iso_alpha2 = country.iso_alpha2
            LEFT OUTER JOIN geonames_admin1_codes_ascii as admin1
                ON admin1.code = CONCAT(city.iso_alpha2, '.', city.admin1_code)
        WHERE %s %s
        ORDER BY city.name ASC %s;
    `, main, secondary, limit)

    nstmt, err := sql.DB.PrepareNamed(query)
    if err != nil {
        return err
    }

    rows, err := nstmt.Queryx(queryParams)
    if err != nil {
        return err
    }

    results := []interface{}{}

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

    b, err := json.Marshal(results)
    if err != nil {
        return err
    }
    w.Write(b)
    return nil
}

使用Postman Chrome插件发送POST请求,并使用以下值:

keyword: "tron"
maxResults: 7
lastRequest: 0

将得到以下JSON输出:

[
    {
        "admin1Name": "VXR0YXJhZGl0",
        "cityId": 1605268,
        "cityName": "VHJvbg==",
        "countryName": "VGhhaWxhbmQ=",
        "searchString": "VHJvbiBUcm9uIFRoYWlsYW5k"
    },
    {
        "admin1Name": "Q2FsYWJyaWE=",
        "cityId": 8949073,
        "cityName": "VHJvbmNh",
        "countryName": "SXRhbHk=",
        "searchString": "VHJvbmNhIFRyb25jYSBJdGFseQ=="
    },
    {
        "admin1Name": "QXJhZ29u",
        "cityId": 3107444,
        "cityName": "VHJvbmNow7Nu",
        "countryName": "U3BhaW4=",
        "searchString": "VHJvbmNow7NuIFRyb25jaG9uIFNwYWlu"
    },
    {
        "admin1Name": "UHVlYmxh",
        "cityId": 8859151,
        "cityName": "VHJvbmNvbmFs",
        "countryName": "TWV4aWNv",
        "searchString": "VHJvbmNvbmFsIFRyb25jb25hbCBNZXhpY28="
    },
    {
        "admin1Name": "U2NobGVzd2lnLUhvbHN0ZWlu",
        "cityId": 2821000,
        "cityName": "VHLDtm5kZWw=",
        "countryName": "R2VybWFueQ==",
        "searchString": "VHLDtm5kZWwgVHJvbmRlbCBHZXJtYW55"
    },
    {
        "admin1Name": "U8O4ci1UcsO4bmRlbGFn",
        "cityId": 3133880,
        "cityName": "VHJvbmRoZWlt",
        "countryName": "Tm9yd2F5",
        "searchString": "VHJvbmRoZWltIFRyb25kaGVpbSBOb3J3YXk="
    },
    {
        "admin1Name": "VG9uZ3Nh",
        "cityId": 1252408,
        "cityName": "VHJvbmdzYQ==",
        "countryName": "Qmh1dGFu",
        "searchString": "VHJvbmdzYSBUcm9uZ3NhIEJodXRhbg=="
    }
]

为什么结果是这样的?将结果放入spew.Dump()中会得到类似以下的输出(我只输出了一行):

(map[string]interface {}) (len=5) {
  (string) (len=11) "countryName": ([]uint8) (len=6 cap=6) {
   00000000  4e 6f 72 77 61 79                                 |Norway|
  },
  (string) (len=12) "searchString": ([]uint8) (len=26 cap=26) {
   00000000  54 72 6f 6e 64 68 65  69 6d 20 54 72 6f 6e 64 68  |Trondheim Trondh|
   00000010  65 69 6d 20 4e 6f 72 77  61 79                    |eim Norway|
  },
  (string) (len=6) "cityId": (int64) 3133880,
  (string) (len=8) "cityName": ([]uint8) (len=9 cap=9) {
   00000000  54 72 6f 6e 64 68 65 69  6d                       |Trondheim|
  },
  (string) (len=10) "admin1Name": ([]uint8) (len=15 cap=15) {
   00000000  53 c3 b8 72 2d 54 72 c3  b8 6e 64 65 6c 61 67     |S..r-Tr..ndelag|
  }
}

我在这里做错了什么

编辑

我尝试按照Elwinar的建议使用结构体但是出现了一些非常奇怪的情况

这段代码可以工作

```go
type City struct {
    AdminName    string `json:"admin1Name"   db:"admin1Name"`
    CityID       int64  `json:"cityId"       db:"cityId"`
    CityName     string `json:"cityName"     db:"cityName"`
    CountryName  string `json:"countryName"  db:"countryName"`
    SearchString string `json:"searchString" db:"searchString"`
}

但是这段代码不工作,并输出错误"missing destination name cityId":

type City struct {
    CityId       int64  `json:"cityId"       db:"cityId"`
    CityName     string `json:"cityName"     db:"cityName"`
    Admin1Name   string `json:"admin1Name"   db:"admin1Name"`
    CountryName  string `json:"countryName"  db:"countryName"`
    SearchString string `json:"searchString" db:"searchString"`
}

有什么区别吗?

解决方案:必须使用空格键分隔标签。不能使用制表符进行空格,也不能使用逗号分隔标签。

英文:

I am using this library from http://jmoiron.github.io/sqlx/ to do a query. Following the documentation led me to the code below.

func cities(w http.ResponseWriter, r *http.Request, _ httprouter.Params) error {
var main string
var secondary string
var limit string
queryParams := make(map[string]interface{})
if k := r.PostFormValue("keyword"); k != "" {
main = "city.name LIKE :keyword"
queryParams["keyword"] = k + "%"
}
if sk := r.PostFormValue("secondaryKeyword"); sk != "" && sk != "null" {
secondary = "OR city.name = :secondaryKeyword"
queryParams["secondaryKeyword"] = sk
}
if mr := r.PostFormValue("maxResults"); mr != "" {
limit = "LIMIT :maxResults"
queryParams["maxResults"] = mr
}
if lr := r.PostFormValue("lastRequest"); lr != "" && lr == "1" {
limit = ""
}
query := fmt.Sprintf(`
SELECT
city.geonameid AS cityId,
city.name AS cityName,
COALESCE(admin1.name_local, '') AS admin1Name,
country.name AS countryName,
CONCAT_WS(' ', city.name, city.asciiname, country.name) AS searchString
FROM geonames_cities1000 AS city
INNER JOIN geonames_countryinfo AS country
ON city.iso_alpha2 = country.iso_alpha2
LEFT OUTER JOIN geonames_admin1_codes_ascii as admin1
ON admin1.code = CONCAT(city.iso_alpha2, '.', city.admin1_code)
WHERE %s %s
ORDER BY city.name ASC %s;
`, main, secondary, limit)
nstmt, err := sql.DB.PrepareNamed(query)
if err != nil {
return err
}
rows, err := nstmt.Queryx(queryParams)
if err != nil {
return err
}
results := []interface{}{}
for rows.Next() {
row := make(map[string]interface{})
err := rows.MapScan(row)
if err != nil {
return err
}
results = append(results, row)
}
b, err := json.Marshal(results)
if err != nil {
return err
}
w.Write(b)
return nil
}

Sending a POST request postman chrome plugin with these values:

keyword: "tron"
maxResults: 7
lastRequest: 0

gave this JSON output:

[
{
"admin1Name": "VXR0YXJhZGl0",
"cityId": 1605268,
"cityName": "VHJvbg==",
"countryName": "VGhhaWxhbmQ=",
"searchString": "VHJvbiBUcm9uIFRoYWlsYW5k"
},
{
"admin1Name": "Q2FsYWJyaWE=",
"cityId": 8949073,
"cityName": "VHJvbmNh",
"countryName": "SXRhbHk=",
"searchString": "VHJvbmNhIFRyb25jYSBJdGFseQ=="
},
{
"admin1Name": "QXJhZ29u",
"cityId": 3107444,
"cityName": "VHJvbmNow7Nu",
"countryName": "U3BhaW4=",
"searchString": "VHJvbmNow7NuIFRyb25jaG9uIFNwYWlu"
},
{
"admin1Name": "UHVlYmxh",
"cityId": 8859151,
"cityName": "VHJvbmNvbmFs",
"countryName": "TWV4aWNv",
"searchString": "VHJvbmNvbmFsIFRyb25jb25hbCBNZXhpY28="
},
{
"admin1Name": "U2NobGVzd2lnLUhvbHN0ZWlu",
"cityId": 2821000,
"cityName": "VHLDtm5kZWw=",
"countryName": "R2VybWFueQ==",
"searchString": "VHLDtm5kZWwgVHJvbmRlbCBHZXJtYW55"
},
{
"admin1Name": "U8O4ci1UcsO4bmRlbGFn",
"cityId": 3133880,
"cityName": "VHJvbmRoZWlt",
"countryName": "Tm9yd2F5",
"searchString": "VHJvbmRoZWltIFRyb25kaGVpbSBOb3J3YXk="
},
{
"admin1Name": "VG9uZ3Nh",
"cityId": 1252408,
"cityName": "VHJvbmdzYQ==",
"countryName": "Qmh1dGFu",
"searchString": "VHJvbmdzYSBUcm9uZ3NhIEJodXRhbg=="
}
]

Why are the results like this? Putting the results in spew.Dump()
will give something like this (I'm just outputting one of the rows):

(map[string]interface {}) (len=5) {
(string) (len=11) "countryName": ([]uint8) (len=6 cap=6) {
00000000  4e 6f 72 77 61 79                                 |Norway|
},
(string) (len=12) "searchString": ([]uint8) (len=26 cap=26) {
00000000  54 72 6f 6e 64 68 65 69  6d 20 54 72 6f 6e 64 68  |Trondheim Trondh|
00000010  65 69 6d 20 4e 6f 72 77  61 79                    |eim Norway|
},
(string) (len=6) "cityId": (int64) 3133880,
(string) (len=8) "cityName": ([]uint8) (len=9 cap=9) {
00000000  54 72 6f 6e 64 68 65 69  6d                       |Trondheim|
},
(string) (len=10) "admin1Name": ([]uint8) (len=15 cap=15) {
00000000  53 c3 b8 72 2d 54 72 c3  b8 6e 64 65 6c 61 67     |S..r-Tr..ndelag|
}

What do I do wrong here?'

EDIT:

I tried doing as Elwinar suggested with structs instead. But something really weird is going on.

This code works:

type City struct {
AdminName    string `json:"admin1Name"   db:"admin1Name"`
CityID       int64  `json:"cityId"       db:"cityId"`
CityName     string `json:"cityName"     db:"cityName"`
CountryName  string `json:"countryName"  db:"countryName"`
SearchString string `json:"searchString" db:"searchString"`
}

But this code does not work and outputs error "missing destination name cityId":

type City struct {
CityId       int64  `json:"cityId"		 db:"cityId"`
CityName     string `json:"cityName"     db:"cityName"`
Admin1Name   string `json:"admin1Name"   db:"admin1Name"`
CountryName  string `json:"countryName"  db:"countryName"`
SearchString string `json:"searchString" db:"searchString"`
}

What is the difference?

Solution: Must separate the tags with space key. Can not do spacing with tab key, and can not use commas to separate tags.

答案1

得分: 5

根据spew.Dump的说明,SQL驱动程序将文本列返回为[]uint8。实际上,它等同于[]byte,而json.Marshal将其编码为base64字符串。

解决这个问题的最简单方法是将行扫描到一个真正的结构体中(使用StructScan在sqlx中可以很好地实现),该结构体将具有string字段,以便json.Marshal将它们显示为您期望的样式。您可以使用标签控制字段的SQL和JSON名称,以便您的实体在每种语言中都具有传统的名称...

示例:

type City struct {
AdminName    string `json:"admin1Name"   sql:"admin1Name"`
CityID       int64  `json:"cityId"       sql:"cityId"`
CityName     string `json:"cityName"     sql:"cityName"`
CountryName  string `json:"countryName"  sql:"countryName"`
SearchString string `json:"searchString" sql:"searchString"`
}
英文:

As spew.Dump tells you, the SQL driver returns []uint8 for textual column. In fact, it is equivalent to []byte, which json.Marshal encode as base64 strings.

The simplest way to work around that is to scan your rows into a real struct (which sqlx does quite well with StructScan), which will have string fields so that json.Marshal will display them as you expect. You can control the name of both the SQL and JSON names of the field using tags, so your entity can have conventionnal names in every language…

Example:

type City struct {
AdminName    string `json:"admin1Name"   sql:"admin1Name"`
CityID       int64  `json:"cityId"       sql:"cityId"`
CityName     string `json:"cityName"     sql:"cityName"`
CountryName  string `json:"countryName"  sql:"countryName"`
SearchString string `json:"searchString" sql:"searchString"`
}

答案2

得分: 3

encoding/json[]byte切片进行Base64编码(参见http://golang.org/pkg/encoding/json/#Marshal)。所以问题只是在通过json.Marshal(results)输出时出现的。

在你的代码中,某个地方调用了一个生成[]byte的函数,这个函数可能被隐藏在其中一个空接口{}中。去掉每个空接口,适当使用字符串,问题要么会消失,要么解决方案会显而易见。

(调用json.Marshal并输出结果以进行调试可能不是一个好主意,特别是如果你不知道自己实际上在进行编组的内容。)

英文:

Package encoding/json will marshal []byte slices base64 encoded (see http://golang.org/pkg/encoding/json/#Marshal). So the problem is just in the output via json.Marshal(results).

Somewhere your code calls a function which produces a []byte which is nicely hidden in one of these {}interfaces . Get rid of each of the empty interfaces, use strings where appropriate and the problem will either vanish or the solution will be obvious.

(Calling json.Marshal and outputting the result for debug purpose might not be a good idea, especially if you do not really know what you are actuall marshaling.)

huangapple
  • 本文由 发表于 2015年9月10日 20:22:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/32501784.html
匿名

发表评论

匿名网友

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

确定