【问题标题】:check json array length without unmarshalling在不解组的情况下检查 json 数组长度
【发布时间】:2022-01-07 13:26:23
【问题描述】:

我有一个请求主体,它是一个 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 请求正文然后循环遍历重新编组每个元素并将其附加到正在发送的任何传出消息来执行此操作。类似的,

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 go httprequest marshalling unmarshalling


    【解决方案1】:

    如果您使用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,并在每次迭代中重用它,调用它的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)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-07
      • 1970-01-01
      • 2016-10-16
      • 2022-10-19
      • 2016-04-08
      • 2022-01-23
      • 1970-01-01
      相关资源
      最近更新 更多