gosqlc

SQLC - how to retrieve data stored as JSONB


I have a simple table that has a field JSONB:

CREATE TABLE IF NOT EXISTS "test_table" (
    "id" text NOT NULL,
    "user_id" text NOT NULL,
    "content" jsonb NOT NULL,
    "create_time" timestamptz NOT NULL,
    "update_time" timestamptz NOT NULL,
    PRIMARY KEY ("id")
);

I used a simple query to generate boilerplate with SQLC.

-- name: GetTestData :one
SELECT * FROM test_table
WHERE id = $1 LIMIT 1;

But the content property gets generated as json.RawMessage.

type TestTable struct {
    ID          string          `json:"id"`
    UserId      string          `json:"user_id"`
    Content     json.RawMessage `json:"content"`
    CreateTime  time.Time       `json:"create_time"`
    UpdateTime  time.Time       `json:"update_time"`
}

Here's a sample of the JSON that is stored inside the content column:

{
  "static": {
    "product": [
      {
        "id": "string",
        "elements": {
          "texts": [
            {
              "id": "string",
              "value": "string"
            }
          ],
          "colors": [
            {
              "id": "string",
              "value": "string"
            }
          ],
          "images": [
            {
              "id": "string",
              "values": [
                {
                  "id": "string",
                  "value": "string"
                }
              ]
            }
          ]
        }
      }
    ]
  },
  "dynamic": {
    "banner": [
      {
        "id": "string",
        "elements": {
          "texts": [
            {
              "id": "string",
              "value": "string"
            }
          ],
          "colors": [
            {
              "id": "string",
              "value": "string"
            }
          ],
          "images": [
            {
              "id": "string",
              "values": [
                {
                  "id": "string",
                  "value": "string"
                }
              ]
            }
          ]
        }
      }
    ]
  }
}

Nested properties inside Static or Dynamic are arrays.

The content property should contain a nested object, and I can't seem to extract the data inside it. json.Unrmarshall() seems get only the top level properties. Is there a way to cast map[string]interface{} to content or to help SQLC generate the property as interface instead of RawMessage?

I tried to solve this just unmarshalling the raw message like so:

var res map[string]json.RawMessage
if err := json.Unmarshal(testingData.Content, &res); err != nil {
    return nil, status.Errorf(codes.Internal, "Serving data err %s", err)
}

var static pb.Static
if err := json.Unmarshal(res["Static"], &static); err != nil {
    return nil, status.Errorf(codes.Internal, "Static data err %s", err)
}
var dynamic pb.Dynamic
if err := json.Unmarshal(res["Dynamic"], &dynamic); err != nil {
    return nil, status.Errorf(codes.Internal, "Dynamic data err %s", err)
}

I'm doing something wrong when unmarshalling the payload but I can't figure out what exactly.

Here's a sample playground: go.dev/play/p/9e7a63hNMEA


Solution

  • Your JSON contains static and dynamic keys. You are parsing into a map[string]json.RawMessage and then trying to retrieve Static and Dynamic from the map (note the capitalisation).

    Fix the map keys (i.e. json.Unmarshal(res["static"], &static))and your code will probably work. A better solution might be to check if the keys exist before attempting to unmarshal them.