无法用gocsv读取带引号的字段

u91tlkcl  于 2023-01-10  发布在  Go
关注(0)|答案(2)|浏览(188)

我有一个csv响应来自一个我不控制的端点,我无法解析它的响应,因为它有引号。它看起来像这样:

name,id,quantity,"status (active, expired)"
John,14,4,active 
Bob,12,7,expired

为了解析这个响应,我创建了以下结构:

type UserInfo struct {
Name     string `csv:"name"`
ID       string `csv:"id"`
Quantity string `csv:"quantity"`
Status   string `csv:"status (active, expired)"`
}

我试过用

Status   string `csv:""status (active, expired)""`
Status   string `csv:'"status (active, expired)"'`

但似乎没有一个是有用的,我只是无法访问字段状态时,我使用gocsv.Unmarshal

var actualResult []UserInfo
err = gocsv.Unmarshal(in, &actualResult)

for _, elem := range actualResult {
    fmt.Println(elem.Status)
    }

但我什么也没得到。
https://go.dev/play/p/lje1zNO9w6E下面是一个示例

kyvafyod

kyvafyod1#

你不需要像gocsv这样的第三方软件包(除非你有特定的用例),因为它可以很容易地用Go语言内置的encoding/csv来完成。
您只需忽略端点响应中的第一行/记录csv header

csvReader := csv.NewReader(strings.NewReader(csvString))

records, err := csvReader.ReadAll()
if err != nil {
    panic(err)
}

var users []UserInfo

// Iterate over all records excluding first one i.e., header
for _, record := range records[1:] {
    users = append(users, UserInfo{Name: record[0], ID: record[1], Quantity: record[2], Status: record[3]})
}

fmt.Printf("%v", users)
// Output: [{ John 14 4 active } { Bob 12 7 expired}]

下面是基于用例和示例字符串的working example on Go Playground

kmb7vmvb

kmb7vmvb2#

我只是不认为gocarina/gocsv可以解析带有引号的逗号的标题。我在文档中没有看到它不能解析的地方,但我做了一些挖掘,有明显的例子表明逗号在“CSV注解”中使用,看起来作者只想到注解中的逗号用于包/API的目的,而不是作为列名的一部分。
如果我们从包中查看sample_structs_test.go,我们可以看到逗号的使用方式如下:

  • 在元数据指令中,如“omitempty”:
type Sample struct {
    Foo  string  `csv:"foo"`
    Bar  int     `csv:"BAR"`
    Baz  string  `csv:"Baz"`
    ...
    Omit *string `csv:"Omit,omitempty"`
}
  • 用于声明结构体中的字段可以从多个不同的标头填充:
type MultiTagSample struct {
    Foo string `csv:"Baz,foo"`
    Bar int    `csv:"BAR"`
}

您可以看到它的实际操作here
FWIW,官方的encoding/json包也有同样的限制,他们注意到了(着重号是加上去的):
每个struct字段的编码都可以通过存储在struct字段标记中“json”键下的格式字符串来定制。格式字符串给出字段的名称,后面可能跟有一个以逗号分隔的选项列表。名称可以为空,以便在指定选项时不覆盖默认字段名称。
以及
如果键名是一个非空字符串,仅包含Unicode字母、数字和ASCII标点符号(引号、反斜杠和逗号除外),则将使用该键名。
因此,您可能无法得到您所期望/想要的:对不起,这可能是对结构体进行注解的能力的限制。2如果你愿意,你可以用gocarina/gocsv来提交一个bug。
与此同时,您可以在头文件传入时对其进行修改。This is example非常笨拙,但它可以正常工作:它只是将“status(active,expired)”替换为“status(active expired)”,并使用没有逗号的版本来注解该结构体。

endpointReader := strings.NewReader(sCSV)

// Fix header
var bTmp bytes.Buffer
fixer := bufio.NewReader(endpointReader)
header, _ := fixer.ReadString('\n')
header = strings.Replace(header, "\"status (active, expired)\"", "status (active expired)", -1)
bTmp.Write([]byte(header))
// Read rest of CSV
bTmp.ReadFrom(fixer)

// Turn back into a reader
reader := bytes.NewReader(bTmp.Bytes())

var actualResult []UserInfo
...

我可以运行它,现在得到:

active 
expired

相关问题