【问题标题】:Reading JSON into GO Strings将 JSON 读入 GO 字符串
【发布时间】:2014-12-11 19:05:16
【问题描述】:

所以我有一个格式为 JSON 的文件...

[
  {
    "Key":"Value",
    "Key2":"Value2",
    "Key3":"Value3"
  },
  {
    "Foo":"Bar",
    "Blah":2
  }
] 

我只想读取其中的哈希部分并将它们传递给一个 HTTP 请求,就像在 goRequest 中一样,因为 goRequest 只需将 JSON 放在字符串中就可以了。

package main
request := gorequest.New()
resp, body, errs := request.Post("http://example.com").
Set("Notes","gorequst is coming!").
Send(`{"Foo":"Bar","Blah":2}`).
End()

我不在乎 JSON 是什么,也不需要将它解组为任何 go Struct 或任何类似的东西,它可以保留为字符串并且完全不受影响,只需传递给请求即可。

我在网上看到了很多关于它的信息,但它似乎总是想将 JSON 解组为 Go Structs 和排序,如果你想关心 JSON 中的实际内容,这很好,但在我的如果这似乎是不必要的开销。

我将如何完成这样的事情?看起来很简单,但现有的 Go 的 JSON 库似乎都无法做到这一点。

谢谢。

【问题讨论】:

  • 我不明白这个问题
  • 也许我不明白您的问题,但如果您只想传递 JSON 文本,则无需使用 JSON 库。
  • 我需要每个单独的哈希都是它自己的请求,所以我只需要获取每个单独的哈希。 JSON 文件很大,我想将其流式传输,因为无法将其加载到内存中。

标签: json rest http go


【解决方案1】:

您可能正在寻找json.RawMessage

对于example

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

func main() {
    txt := []byte(`
    [
      {"key1"  : "value1" },
      {"key2"  : "value2" }
    ]`)
    msg := []json.RawMessage{}
    err := json.Unmarshal(txt, &msg)
    if err != nil {
        log.Fatal(err)
    }
    for _, c := range msg {
        fmt.Printf("%s\n", string(c))
    }
}

请注意,示例中分隔键/值对的冗余空格是有意的:您会看到这些空格保留在输出中。

或者,即使您不关心确切的结构,您仍然可以使用interface{} 变量动态地查看它。在 Generic JSON with interface{} 部分下,请参阅 JSON and Go 文档以获取运行示例。

如果我们尝试做一些类似流式处理的方法,我们可能会尝试使用io.Reader 做一些自定义的事情。 JSON 解析器假定您可以一次表示内存中的所有内容。这种假设可能不适用于您的情况,所以我们必须打破一些东西。

也许我们可能会手动消耗io.Reader 中的字节,直到我们吃掉前面的[ 字符,然后在io.Reader 的其余部分重复调用json.Decode。类似this:

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
)

func main() {
    var txt io.Reader = bytes.NewBufferString(`
    [
      {"key1"  : "value1" },
      {"key2"  : "value2" }
    ]`)
    buf := make([]byte, 1)
    for {
        _, err := txt.Read(buf)
        if err != nil {
            log.Fatal(err)
        }
        if buf[0] == '[' {
            break
        }
    }
    for {
        decoder := json.NewDecoder(txt)
        msg := json.RawMessage{}
        err := decoder.Decode(&msg)
        if err != nil {
            break
        }
        fmt.Printf("I see: %s\n", string(msg))

        txt = decoder.Buffered()
        for {
            _, err := txt.Read(buf)
            if err != nil {
                log.Fatal(err)
            }
            if buf[0] == ',' || buf[0] == ']' {
                break
            }
        }

    }
}

这段代码非常笨拙且不明显。我也不认为这是一个好主意。如果您必须以流方式处理此问题,那么 JSON 可能不是这种情况下的良好序列化格式。如果您可以控制输入,那么您应该考虑对其进行更改,使其更适合流式处理:像我们在这里所做的那样的黑客行为是输入格式错误的恶臭。

【讨论】:

  • 谢谢 Dyoo,这太棒了!我实际上可以将输入格式更改为 CSV,这可能更容易流式传输,但我必须获取标题并将它们映射到每一行的值,所以我想毕竟我将不得不编码一些 JSON来自 CSV,以便以流方式进行这项工作。标准的 CSV 包能做到这一点吗?
  • 一种可能性:您可能会考虑更改输入,使其由一组按顺序排列的单独 JSON 对象组成,但不在数组中,而只是在字节流中按顺序排列。您会注意到,原始方法中的所有hackery 只是为了处理由于位于显式数组中而放置在那里的人工句法结构。
  • 另请参阅:developers.google.com/protocol-buffers/docs/… 中的类似方法。您遇到的问题很常见,解决方案也很常见:与其构建一个大型结构并通过网络发送,不如发送每个单独的部分,以某种方式描述。
  • 我在下面提出了一种解决方案,正是您的建议。基本上不是尝试将所有 CSV 转换为 JSON,而是获取每个 CSV 实例并将其转换为 JSON,然后触发请求。代码如下,和你说的一样吗?
  • @stephen 我不这么认为。我添加了评论,因为不幸的是,提问者似乎误解了我的意思。 CSV 可能不是正确的信封,因为内容可能包含任意结构。当我说:“JSON 不是正确的序列化格式”时,我应该更清楚:各个消息块可以按照您想要的任何方式进行编码,并且 JSON 在那里应该没问题。这是对 stream 进行编码的问题,尝试将 stream 编码为一个 JSON 对象可能不是一个好主意,因为大多数 JSON 解析器会尝试读取整个对象一次。
【解决方案2】:

这是我想的解决方案,这看起来合理吗?

package main
import (
        "encoding/csv"
        "fmt"
        "os"
    "bytes"
    "flag"
    "github.com/parnurzeal/gorequest"
)
func process_line(headers []string, line []string) {
    var comma string = ""
    var buffer bytes.Buffer
    buffer.WriteString("[{")
        for i := range headers {
            buffer.WriteString(fmt.Sprintf("%s\"%s\":\"%s\"", comma, headers[i], line[i]))
                comma = ","
        }
        fmt.Fprintf(&buffer,"}]\n")
    request := gorequest.New()
    resp, body, errs := request.Post("www.something.com").
                Set("Content-Type", "application/json").
                Set("Accept", "application/json").
                Send(buffer.String()).End()
    if errs == nil {
        return resp
    }else{
        fmt.Println(errs)
    }
}
func main() {
    file := flag.String("file", "", "Filename?")
    flag.Parse()

    if *file == "" {
        fmt.Println("No file specified. :-(")
        os.Exit(1)
    }
        csvFile, err := os.Open(*file)
        if err != nil {
        fmt.Println(err)
        }
        defer csvFile.Close()
        reader := csv.NewReader(csvFile)
    var i int = 0
    var headers []string
    for {
        line, err := reader.Read()
        if err != nil {
            break
        }
        if i == 0 {
            headers = line
        }else{
            go process_line(headers, line)
        }
        if i%100 == 0 {
            fmt.Printf("%v records processed.\n", i)
        }
        i += 1
    }
}

【讨论】:

    猜你喜欢
    • 2021-02-25
    • 1970-01-01
    • 1970-01-01
    • 2016-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多