Gorm在自定义字符串类型上返回Scanner错误。

huangapple go评论84阅读模式
英文:

Gorm returns Scanner error on custom string type

问题

我写了下面的实体:

type DataCategory string

const (
	DataCategory1 DataCategory = "Category1"
	DataCategory2 DataCategory = "Category2"
)

type Data struct {
	Name       string         `json:"name"`
	Categories []DataCategory `json:"category" gorm:"type:text[]"`
}

我手动在数据库中创建了一行,并将categories数组类型填充为Category1和Category2。

但是在读取数据时出现以下错误:

sql: Scan error on column index 19, name "category": unsupported Scan, storing driver.Value type string into type *[]DataCategory

示例Value方法:

func (s DataCategorySlice) Value() (driver.Value, error) {
	if s == nil {
		return nil, nil
	}
	if len(s) == 0 {
		return "{}", nil
	}

	v := []string{}
	for _, dc := range s {
		v = append(v, string(dc))
	}
	result := fmt.Sprintf("{%s}", strings.Join(v, ","))
	return result, nil
}
英文:

I wrote the below entity:

type DataCategory string

const (
	DataCategory1 DataCategory = "Category1"
	DataCategory2 DataCategory = "Category2"
)

type Data struct {
	Name            string         `json:"name"`
	Categories      []DataCategory `json:"category" gorm:"type:text[]"`
}

I manually created a row in the database and filled the categories array type with Category1 and Category2.

But this gives the below error when reading the data:

sql: Scan error on column index 19, name "category": unsupported Scan, storing driver.Value type string into type *[]DataCategory

Example Value method:

func (s DataCategorySlice) Value() (driver.Value, error) {
	if s == nil {
		return nil, nil
	}
	if len(s) == 0 {
		return "{}", nil
	}

	v := []string{}
	for _, dc := range s {
		v = append(v, string(dc))
	}
	result := fmt.Sprintf("{%s}", strings.Join(v, ","))
	return result, nil
}

答案1

得分: 1

以下是翻译好的内容:

下面的示例假设您正在使用PostgreSQL作为关系数据库管理系统,并且DataCategory的值不包含逗号或未转义的单引号。

// 声明自定义类型
type DataCategorySlice []DataCategory
// 实现driver.Valuer接口,将值编码为目标RDBMS接受的正确格式
func (s DataCategorySlice) Value() (driver.Value, error) {
    if s == nil {
        return nil, nil
    }
    if len(s) == 0 {
        return []byte(`{}`), nil
    }

    v := []byte(`{`)
    for i := range s {
        v = append(v, s[i]...)
        v = append(v, ',')
    }
    v[len(v)-1] = '}' // 用闭合括号替换最后一个逗号
    return v, nil
}
// 实现scanner接口,将从数据库检索到的原始源值解码
func (s *DataCategorySlice) Scan(src any) error {
    var data []byte
    switch v := src.(type) {
    case []byte:
        data = v
    case string:
        data = []byte(v)
    case nil:
        return nil
    default:
        return fmt.Errorf("不支持的类型:%T", src)
    }
    if len(data) == 0 {
        return nil
    }
    data = data[1:len(data)-1] // 移除周围的大括号
    for _, v := range bytes.Split(data, []byte{','}) {
        *s = append(*s, DataCategory(v))
    }
    return nil
}

或者,如果从Value() (driver.Value, error)返回[]byte不起作用,例如导致"ERROR: malformed array literal: <hex representation of the field's value> (SQLSTATE 22P02)",那么您可以尝试将返回类型更改为string

来自@kozhioyrin的示例

// 实现driver.Valuer接口,将值编码为目标RDBMS接受的正确格式
func (s DataCategorySlice) Value() (driver.Value, error) {
	if s == nil {
		return nil, nil
	}
	if len(s) == 0 {
		return "{}", nil
	}

	v := []string{}
	for _, dc := range s {
		v = append(v, string(dc))
	}
	result := fmt.Sprintf("{%s}", strings.Join(v, ","))
	return result, nil
}
英文:

The following example assumes that you're using PostgreSQL as the RDBMS and that the DataCategory values do not contain commas nor unescaped single quotes.

// declare the custom type
type DataCategorySlice []DataCategory
// implement driver.Valuer to encode the value into the
// correct format that is accepted by the target RDBMS
func (s DataCategorySlice) Value() (driver.Value, error) {
    if s == nil {
        return nil, nil
    }
    if len(s) == 0 {
        return []byte(`{}`), nil
    }

    v := []byte(`{`)
    for i := range s {
        v = append(v, s[i]...)
        v = append(v, &#39;,&#39;)
    }
    v[len(v)-1] = &#39;}&#39; // replace last comma with closing brace
    return v, nil
}
// implement scanner to decode the raw source
// value as retrieved from the database
func (s *DataCategorySlice) Scan(src any) error {
    var data []byte
    switch v := src.(type) {
    case []byte:
        data = v
    case string:
        data = []byte(v)
    case nil:
        return nil
    default:
        return fmt.Errorf(&quot;unsupported type: %T&quot;, src)
    }
    if len(data) == 0 {
        return nil
    }
    data = data[1:len(data)-1] // remove surrounding braces
    for _, v := range bytes.Split(data, []byte{&#39;,&#39;}) {
        *s = append(*s, DataCategory(v))
    }
    return nil
}

Alternatively, if returning []byte from Value() (driver.Value, error) doesn't work, e.g. it results in &quot;ERROR: malformed array literal: &lt;hex representation of the field&#39;s value&gt; (SQLSTATE 22P02)&quot;, then you can try using string as the return type instead.

<sub>example from @kozhioyrin</sub>

// implement driver.Valuer to encode the value into the
// correct format that is accepted by the target RDBMS
func (s DataCategorySlice) Value() (driver.Value, error) {
	if s == nil {
		return nil, nil
	}
	if len(s) == 0 {
		return &quot;{}&quot;, nil
	}

	v := []string{}
	for _, dc := range s {
		v = append(v, string(dc))
	}
	result := fmt.Sprintf(&quot;{%s}&quot;, strings.Join(v, &quot;,&quot;))
	return result, nil
}

huangapple
  • 本文由 发表于 2023年4月11日 23:46:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/75987793.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定