如何在Go中读取JSON对象而不进行解码(用于读取大数据流)
我正在读取JSON以响应HTTP端点,并希望提取嵌套在其中的对象数组的内容。响应可能很大,所以我尝试使用流式方法,而不仅仅是json。JSON看起来是这样的:如何在Go中读取JSON对象而不进行解码(用于读取大数据流),json,go,stream,decode,unmarshalling,Json,Go,Stream,Decode,Unmarshalling,我正在读取JSON以响应HTTP端点,并希望提取嵌套在其中的对象数组的内容。响应可能很大,所以我尝试使用流式方法,而不仅仅是json。JSON看起来是这样的: { "useless_thing_1": { /* etc */ }, "useless_thing_2": { /* etc */ }, "the_things_i_want": [ { /* complex object I want to json.Unmarshal #1 */ }, { /* complex object
{
"useless_thing_1": { /* etc */ },
"useless_thing_2": { /* etc */ },
"the_things_i_want": [
{ /* complex object I want to json.Unmarshal #1 */ },
{ /* complex object I want to json.Unmarshal #2 */ },
{ /* complex object I want to json.Unmarshal #3 */ },
/* could be many thousands of these */
],
"useless_thing_3": { /* etc */ },
}
Go提供的json库具有json.Unmarshal,它适用于完整的json对象。它还具有json.Decoder,可以解组完整对象或提供单个标记。我可以使用这个标记器仔细检查并提取内容,但这样做的逻辑有点复杂,在我将其作为标记读取之后,我无法轻松地在对象上使用json.Unmarshal
json.Decoder是缓冲的,这使得读取一个对象(即{/*我想要json.Unmarshal 1*/}的复杂对象,然后使用,我自己,并创建一个新的json.Decoder)变得很困难,因为它将尝试使用逗号本身。这是我尝试过的方法,但没有成功
我正在寻找解决这个问题的更好办法。下面是我试图手动使用逗号时出现的错误代码:
//这里的代码天真地寻找“我想要的东西:[”和
//将其后面的下一个字节放入'buffer'中`
//这是从`{/*复杂对象开始的流的其余部分,我想要json.Unmarshal 1*/}`
in:=io.MultiReaderbuffer,res.Body
dec:=json.NewDecoderin
为了{
var p MyComplexThing
错误:=dec.Decode&p
如果错误!=零{
圆锥花序
}
//直接从中窃取逗号-这不起作用,因为解码器缓冲区是它的输入
变量b1[1]字节
_,err=io.readAtlastin,b1[:],1//返回流中后面部分的随机数据
如果错误!=零{
圆锥花序
}
开关b1[0]{
案例',':
//跳过它
案例']':
休息//我们结束了
违约:
从读取%v,b1得到的预期结果
}
}
使用和将JSON文档解码为流
浏览文档,了解感兴趣的JSON值。调用unmarshal将JSON值转换为Go值。根据需要重复上述步骤,以清除所有感兴趣的值
下面是一些带有注释的代码,解释了它的工作原理:
func decode(r io.Reader) error {
d := json.NewDecoder(r)
// We expect that the JSON document is an object.
if err := expect(d, json.Delim('{')); err != nil {
return err
}
// While there are fields in the object...
for d.More() {
// Get field name
t, err := d.Token()
if err != nil {
return err
}
// Skip value if not the field that we are looking for.
if t != "the_things_i_want" {
if err := skip(d); err != nil {
return err
}
continue
}
// We expect JSON array value for the field.
if err := expect(d, json.Delim('[')); err != nil {
return err
}
// While there are more JSON array elements...
for d.More() {
// Unmarshal and process the array element.
var m map[string]interface{}
if err := d.Decode(&m); err != nil {
return err
}
fmt.Printf("found %v\n", m)
}
// We are done decoding the array.
return nil
}
return errors.New("things I want not found")
}
// skip skips the next value in the JSON document.
func skip(d *json.Decoder) error {
n := 0
for {
t, err := d.Token()
if err != nil {
return err
}
switch t {
case json.Delim('['), json.Delim('{'):
n++
case json.Delim(']'), json.Delim('}'):
n--
}
if n == 0 {
return nil
}
}
}
// expect returns an error if the next token in the document is not expectedT.
func expect(d *json.Decoder, expectedT interface{}) error {
t, err := d.Token()
if err != nil {
return err
}
if t != expectedT {
return fmt.Errorf("got token %v, want token %v", t, expectedT)
}
return nil
}
.用于浏览文档到感兴趣的点。调用unmarshal将JSON值转换为Go值。根据需要重复此操作以清除所有感兴趣的值。有关跳过任意JSON值的函数,请参阅。将一个简单的JSON.Unmarshal转换为一个结构变量,该结构变量只声明一个字段作为“我想要的东西”,应该也能起作用:它当然会读取所有无用的东西并对其进行词法分析。您也必须这样做,但不能对其进行解组。@Volker是的,我明白,但这意味着我必须对数据进行处理,直到所有数据都被解封,最好是流式处理。这里有三件事:1。你必须阅读你想要处理的东西,任何技术都不能让你省掉这些。2.您必须至少在感兴趣的部分结束之前解码JSON,同样,没有任何技术可以为您提供此功能。3.你必须至少解开你感兴趣的部分,没有任何技术可以让你做到这一点。