检查 json 数组长度而不解组

check json array length without unmarshalling

我有一个请求主体,它是一个 json 对象数组,例如,

    {
        "data": [
            {
                "id": "1234",
                "someNestedObject": {
                    "someBool": true,
                    "randomNumber": 488
                },
                "timestamp": "2021-12-13T02:43:44.155Z"
            },
            {
                "id": "4321",
                "someNestedObject": {
                    "someBool": false,
                    "randomNumber": 484
                },
                "timestamp": "2018-11-13T02:43:44.155Z"
            }
        ]
    }

我想获取数组中对象的计数并将它们拆分为单独的 json 输出以传递到下一个服务。我通过解组原始 json 请求正文然后遍历元素再次编组每个元素并将其附加到正在发送的任何传出消息来执行此 atm。像,

requestBodyBytes := []bytes(JSON_INPUT_STRING)

type body struct {
    Foo []json.RawMessage `json:"foo"`
}

var inputs body

_ = json.Unmarshal(requestBodyBytes, &inputs)

for input := range inputs {
    re, _ := json.Marshal(m)

    ... do something with re
}

尽管字符串表示形式相同,但我看到的是前后的字节数组不同。我想知道是否有一种方法可以做到这一点而不改变编码或这里发生的任何事情来改变字节以防止任何不需要的突变?数组中的实际 json 对象都将具有不同的形状,因此我无法使用带有字段验证的结构化 json 定义来提供帮助。

另外,上面的代码只是一个例子,所以如果有拼写或语法错误,请忽略它们,因为实际代码的工作方式与描述的一样。

如果您使用 json.RawMessage,JSON 源文本将不会被解析,而是按原样存储在其中(它是 []byte)。

所以如果你想分发相同的 JSON 数组元素,你不需要对它做任何事情,你可以按原样“移交”。您不必将它传递给 json.Marshal(),它已经是 JSON 编组文本。

所以简单地做:

for _, input := range inputs.Foo {
    // input is of type json.RawMessage, and it's already JSON text
}

如果您将 json.RawMessage 传递给 json.Marshal(),它可能会被重新编码,例如压缩(这可能会导致不同的字节序列,但它将包含与 JSON 相同的数据)。

压缩甚至可能是个好主意,因为从原始上下文(对象和数组)中取出原始缩进可能看起来很奇怪,而且它会更短。要简单地压缩 JSON 文本,您可以像这样使用 json.Compact()

for _, input := range inputs.Foo {
    buf := &bytes.Buffer{}
    if err := json.Compact(buf, input); err != nil {
        panic(err)
    }
    fmt.Println(buf) // The compacted array element value
}

如果您不想压缩它而是想自己缩进数组元素,请像这样使用 json.Indent()

for _, input := range inputs.Foo {
    buf := &bytes.Buffer{}
    if err := json.Indent(buf, input, "", "  "); err != nil {
        panic(err)
    }
    fmt.Println(buf)
}

使用您的示例输入,这是第一个数组元素的样子(原始、压缩和缩进):

Orignal:
{
            "id": "1234",
            "someNestedObject": {
                "someBool": true,
                "randomNumber": 488
            },
            "timestamp": "2021-12-13T02:43:44.155Z"
        }

Compacted:
{"id":"1234","someNestedObject":{"someBool":true,"randomNumber":488},"timestamp":"2021-12-13T02:43:44.155Z"}

Indented:
{
  "id": "1234",
  "someNestedObject": {
    "someBool": true,
    "randomNumber": 488
  },
  "timestamp": "2021-12-13T02:43:44.155Z"
}

尝试 Go Playground 上的示例。

另请注意,如果您决定压缩或缩进循环中的各个数组元素,您可以创建一个简单的 bytes.Buffer before the loop, and reuse it in each iteration, calling its Buffer.Reset() 方法来清除先前数组的数据。

它可能看起来像这样:

buf := &bytes.Buffer{}
for _, input := range inputs.Foo {
    buf.Reset()
    if err := json.Compact(buf, input); err != nil {
        panic(err)
    }
    fmt.Println("Compacted:\n", buf)
}