将JSON读入GO字符串

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

Reading JSON into GO Strings

问题

所以我有一个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结构体或类似的东西,只需将其保留为字符串并完全不变,然后传递给请求即可。

我在网上看到了很多相关内容,但它们似乎总是想要将JSON解析为Go结构体之类的东西,如果你想关心JSON的实际内容,这样做是可以的,但在我的情况下,这似乎是不必要的开销。

我该如何实现这样的功能?这似乎很简单,但是现有的Go JSON库似乎无法实现这一点。

谢谢。

英文:

So I have a JSON File in the format...

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

I want to just read in the hash parts of it and pass them to an HTTP request like in goRequest, because goRequest is fine with just the JSON being in a String.

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

I don't care what the JSON is and I don't need to unmarshal it to any go Structs or anything of the sort, it's fine just remaining as a string and being totally untouched, just passed along to the request.

I've seen a lot online about it, but it always seems to wanna un-marshal the JSON to Go Structs and the sort, which is fine if you want to care about what actually is in the JSON, but in my case this seems like unnecessary overhead.

How would I accomplish something like this? It seems pretty simple, but none of the existing JSON libraries for Go seem to be able to accomplish this.

Thanks.

答案1

得分: 3

你可能正在寻找 json.RawMessage

例如:

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{} 变量动态地访问它。在 JSON and Go 文档中有一个运行示例,介绍了“Generic JSON with interface”部分。

如果我们尝试像流式处理一样处理数据,可以尝试使用 io.Reader 进行自定义操作。JSON 解析器假设你可以一次性将所有内容表示在内存中。在你的情况下,这个假设可能不成立,所以我们需要做一些调整。

也许我们可以手动消耗 io.Reader 中的字节,直到读取到开头的 [ 字符,然后在剩余的 io.Reader 上重复调用 json.Decode。类似于 这样

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 可能不是适合这种情况的序列化格式。如果你可以控制输入,那么你应该考虑更改它,使其更适合流式处理:像我们在这里做的这种 hack 是输入数据形状错误的一个坏迹象。

英文:

You are probably looking for json.RawMessage.

For 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))
	}
}

Note that the redundant white space in the example separating the key/value pairs is intentional: you will see that these are preserved in the output.

Alternatively, even if you don't care about the exact structure, you can still dynamically poke at it by using an interface{} variable. See the JSON and Go document for a running example of this, under the Generic JSON with interface{} section.

If we are trying to do something like a streaming approach, we may attempt to do something custom with the io.Reader. The JSON parser assumes you can represent everything in memory at once. That assumption may not hold in your situation, so we have to break a few things.

Perhaps we might manually consume bytes in the io.Reader till we eat the leading [ character, and then repeatedly call json.Decode on the rest of the io.Reader. Something like 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
			}
		}

	}
}

This code is severely kludgy and non-obvious. I also don't think it's a good idea. If you have to deal with this in a streaming fashion, then JSON is likely not a good serialization format for this scenario. If you have control over the input, then you should consider changing it so it's more amendable to a streaming approach: hacks like what we're doing here are a bad smell that the input is in the wrong shape.

答案2

得分: 0

以下是我翻译好的代码:

package main

import (
	"bytes"
	"encoding/csv"
	"flag"
	"fmt"
	"os"

	"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
	}
}

希望对你有帮助!

英文:

Here is what I was thinking as a solution, does this look sane?

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
}
}

huangapple
  • 本文由 发表于 2014年12月12日 03:05:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/27430485.html
匿名

发表评论

匿名网友

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

确定