如何使用 Golang 解码这个嵌套的 json?

How to decode this nested json with Golang?

提问人:markhorrocks 提问时间:11/17/2023 最后编辑:markhorrocks 更新时间:11/18/2023 访问量:122

问:

我正在尝试将嵌套的 json 解码为包含文件和数据的请求的一部分。

数据如下所示

{data: {"date_required":null}}

我最初没有包含完整的错误,因为我忘记记录它。

2023/11/17 23:40:35 error in decoding request body data
2023/11/17 23:40:35 invalid character '.' looking for beginning of value

我相信这个错误可能是由于表单数据不是JSON造成的,但不知道如何解决它。我的 Flutter 代码在我看来就像它发送了有效的 JSON。Content-Type 是,这可能是导致错误的原因。我相信我的代码的文件上传部分需要这个 Content-Type。multipart/form-data

该请求来自我的 Flutter 客户端,代码如下:

final multipartFile =
    http.MultipartFile.fromBytes('file', bytes, filename: file?.name);

final request = http.MultipartRequest('POST', Uri.parse(user.fileUrl));

request.files.add(multipartFile);

request.headers.addAll(headers);

String dateRequiredStr = dateRequired != null
    ? jsonEncode({'date_required': dateRequired})
    : jsonEncode({'date_required': null});

request.fields['data'] = dateRequiredStr;

在我的go API中,我正在这样做。

模型(根据下面的答案编辑):

type FileRequiredDate struct {
    DateRequired pgtype.Date `json:"date_required"`
}

type FileRequiredDateData struct {
    Data FileRequiredDate `json:"data"`
}

法典:

func (rs *appResource) uploadTranscriptAudioFile(w http.ResponseWriter, r *http.Request) {

start := time.Now()

const maxUploadSize = 500 * 1024 * 1024 // 500 Mb

var requiredByDate FileRequiredDateData

decoder := json.NewDecoder(r.Body)

err := decoder.Decode(&requiredByDate)

if err != nil {
    log.Println("error in decoding request body data")
    log.Println(err.Error())
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
}

file, handler, err := r.FormFile("file")

if err != nil {
    log.Println(err)
    http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
    return
}

defer file.Close()

fileSize := handler.Size

if fileSize > maxUploadSize {
    http.Error(w, "FILE_TOO_BIG", http.StatusBadRequest)
    return
}

fileName := handler.Filename

httputil.DumpRequest-> 内容类型:multipart/form-data

编辑:在回答这个问题之后,我编辑了这样的代码:

mr, err := r.MultipartReader()

if err != nil {
    log.Println(err)
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

for {
    part, err := mr.NextPart()

    // This is OK, no more parts
    if err == io.EOF {
        break
    }

    // Some error
    if err != nil {
        log.Println("multipart reader other error")
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    log.Println(part.FormName())

    if part.FormName() == "data" {

        log.Println("multipart reader found multipart form name data")

        decoder := json.NewDecoder(r.Body)

        err = decoder.Decode(&requiredByDate)

        if err != nil {
            log.Println("error in decoding request body data")
            log.Println(err.Error())
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
    }
}

    if part.FormName() == "file" {

        file, handler, err := r.FormFile("file")  <-- error here

        if err != nil {
            log.Println("error getting form file")
            log.Println(err.Error())
            http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusInternalServerError)
            return
        }

        defer file.Close()

        guid := xid.New()

        userId := getUserFromJWT(r)

        user, err := getUser(rs, int64(userId))

        if err != nil {
            log.Println("user not found")
            log.Println(err.Error())
            http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
            return
        }

        err = uploadToMinio(rs, file, fileSize, fileName, guid.String(), userId)


----

这给了我这个输出:

2023/11/17 23:23:30 data
2023/11/17 23:23:30 file

编辑:

我通过使用代替decoder := json.NewDecoder(part)decoder := json.NewDecoder(r.Body)

现在我得到了.似乎我应该以某种方式使用 part,但 part 没有文件属性。 r.Body 不再可用,因为我将表单数据添加到了 mulitpart 请求中。这似乎是一个不同的问题。error getting form file

评论

0赞 mkopriva 11/17/2023
FileRequiredDateData的结构与 JSON 的结构不匹配。该类型有两个单独的“同级”字段;而不是像 JSON 那样嵌套在另一个中。
0赞 markhorrocks 11/17/2023
我不知道如何在模型中表示该json字符串。
0赞 segFault 11/17/2023
请提供特定于请求处理的更多代码。
0赞 markhorrocks 11/17/2023
没有更多特定于请求处理的代码,除非您想要我知道有效的文件处理代码。
4赞 Peter 11/17/2023
您正在尝试将 multipart/form-data 解码为 JSON。将 Request.MultipartReader 或 Request.MultipartForm 与 Request.ParseMultipartForm 一起使用,然后仅解码 JSON 部分。

答:

2赞 segFault 11/17/2023 #1

虽然这不会解决 404 问题(请使用请求处理程序代码更新您的问题),但您的结构似乎与您发送的内容不匹配。你可以做这样的事情来解决这个问题:

type FileRequiredDate struct {
    DateRequired pgtype.Date `json:"date_required"`
}

type FileRequiredDateData struct {
    Data FileRequiredDate `json:"data"`
}

这应该按预期解码请求正文。

至于 404,您应该仔细检查客户端代码发送的请求路径和方法是否与服务器请求处理程序路径和方法匹配。

评论

0赞 markhorrocks 11/17/2023
我使用了您的代码,但出现解析错误,如更新的问题中所述。原始 404 错误由我硬编码以指示解析错误。
0赞 mkopriva 11/17/2023
@markhorrocks 题中的代码是硬编码的,这是和不是。您的声明和代码不太一致,这使得提出准确的解决方案变得更加困难。此外,在您更新的问题中,您没有“描述”错误;我们不知道您是真的指的是“解析错误”(这表示传入的 JSON 无效),还是您的意思是“解码错误”,这可能表明 JSON 值仍然与目标类型不匹配。StatusBadRequest400404
0赞 mkopriva 11/17/2023
@markhorrocks鉴于您未能提供有关您遇到的问题的足够信息,我只能猜测问题可能是什么:一个原因可能是并且不能很好地配合在一起,有时指针(即)可以解决该问题。另一个原因可能只是不支持 JSON 中发送的 JSON 数据格式,在这种情况下,不应使用可以处理传入 JSON 值的类型。nullpgtype.Date*pgtype.Datepgtype.Datepgtype.Date
0赞 markhorrocks 11/17/2023
我已经提供了相关代码的每一行。我的模型中有数百个模型,它们可以很好地处理 Flutter 帖子中的空数据。我不知道解码和 json 解析错误之间甚至存在差异。这就是这个网站存在的原因。pgtype.Date
0赞 mkopriva 11/17/2023
@markhorrocks代码在这一点上是无关紧要的;显示确切的错误消息是什么,输出是什么?log.Println(err.Error())