You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法将JSON范围反序列化为pgtype.Int4range的技术问询

pgtype.Int4range的JSON反序列化问题解决

问题描述

尝试将JSON中的范围字符串(如"[20,30)")反序列化为pgtype.Int4range类型时,调用json.Unmarshal报错,提示无法将字符串解析为pgtype.Int4range类型。想确认该类型是否仅用于数据库操作,能否实现JSON反序列化(使用pgx v4)。

测试代码

import (
    "encoding/json"
    "testing"

    "github.com/jackc/pgtype"
    "github.com/stretchr/testify/assert"
)

type TestHealthPreference struct {
    HealthRange pgtype.Int4range `json:"health_range"`
    ID          string           `json:"id"`
}

// 验证反序列化是否正常的测试函数
func TestPreferenceUpdateUnmarshal(t *testing.T) {
    jsonData := `{
        "health_range": "[20,30)",
        "id": "123"
    }`

    var update TestHealthPreference
    err := json.Unmarshal([]byte(jsonData), &update)
    if err != nil {
        t.Errorf("JSON反序列化出错: %v", err)
    }

    assert.Equal(t, 20, update.HealthRange.Lower)
}

报错信息

Error while unmarshalling JSON: json: cannot unmarshal string into Go struct field TestPreference.health_range of type pgtype.Int4range.

原因分析

pgtype包中的类型(包括Int4range)默认仅实现了数据库交互所需的sql.Scanner和driver.Valuer接口,并没有实现encoding/json要求的json.Marshaler和json.Unmarshaler接口,所以直接用json.Unmarshal处理会报错。但该类型并非只能用于数据库操作,通过自定义扩展可以实现JSON编解码。

解决方案

方案1:自定义类型并实现JSON编解码接口

通过嵌套pgtype.Int4range定义自定义类型,手动实现JSON编解码逻辑:

import (
    "encoding/json"
    "fmt"
    "github.com/jackc/pgtype"
)

// 自定义Int4Range类型,嵌套pgtype.Int4range
type Int4Range pgtype.Int4range

// 实现UnmarshalJSON方法,解析JSON字符串为Int4Range
func (r *Int4Range) UnmarshalJSON(data []byte) error {
    var rangeStr string
    if err := json.Unmarshal(data, &rangeStr); err != nil {
        return err
    }

    var pgRange pgtype.Int4range
    // 利用pgtype内置的Set方法解析PostgreSQL格式的范围字符串
    if err := pgRange.Set(rangeStr); err != nil {
        return fmt.Errorf("无效的int4range格式: %w", err)
    }

    *r = Int4Range(pgRange)
    return nil
}

// 可选:实现MarshalJSON方法,将Int4Range序列化为JSON字符串
func (r Int4Range) MarshalJSON() ([]byte, error) {
    pgRange := pgtype.Int4range(r)
    if !pgRange.Valid {
        return []byte("null"), nil
    }
    // 获取PostgreSQL格式的范围字符串
    rangeStr, err := pgRange.Value()
    if err != nil {
        return nil, err
    }
    return json.Marshal(rangeStr)
}

修改结构体中的类型为自定义的Int4Range:

type TestHealthPreference struct {
    HealthRange Int4Range `json:"health_range"`
    ID          string    `json:"id"`
}

方案2:先反序列化为字符串,再手动转换

先将JSON范围字段反序列化为普通字符串,再通过pgtype.Int4range的Set方法转换为目标类型:

// 临时结构体,用于接收JSON数据
type TestHealthPreferenceTemp struct {
    HealthRange string `json:"health_range"`
    ID          string `json:"id"`
}

func TestPreferenceUpdateUnmarshal(t *testing.T) {
    jsonData := `{
        "health_range": "[20,30)",
        "id": "123"
    }`

    var temp TestHealthPreferenceTemp
    err := json.Unmarshal([]byte(jsonData), &temp)
    if err != nil {
        t.Errorf("JSON反序列化出错: %v", err)
    }

    // 手动转换为pgtype.Int4range
    var healthRange pgtype.Int4range
    if err := healthRange.Set(temp.HealthRange); err != nil {
        t.Errorf("解析Int4range出错: %v", err)
    }

    var update TestHealthPreference
    update.HealthRange = healthRange
    update.ID = temp.ID

    // 注意:pgtype.Int4range的Lower是pgtype.Int4类型,需访问其Int字段(int32类型)
    assert.Equal(t, int32(20), update.HealthRange.Lower.Int)
}

注意事项

pgtype.Int4range的Lower和Upper字段是pgtype.Int4类型,而非原生int类型,断言时需要访问其Int属性(类型为int32),否则会因类型不匹配导致断言失败。

内容的提问来源于stack exchange,提问作者mistermims

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 14:50:34