要するに
entgoで生成されるモデル構造体からjson:"omitempty"
を駆逐するためのライブラリを作成しました。
困ったこと
goのORMにent.を使用し、スキーマファイルからモデル構造体を自動生成する際、
生成される構造体にはjson:"omitempty"
のタグが自動で付与されています。
例えばこのようなスキーマを作成して、
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// Pet holds the schema definition for the Pets entity.
type Pet struct {
ent.Schema
}
// Fields of the Pet.
func (Pet) Fields() []ent.Field {
return []ent.Field{
field.Uint32("id"),
field.Uint32("sex"),
}
}
モデル構造体の自動生成(go generate ./ent
)を行うと、以下のような構造体が生成されます。
// Pet is the model entity for the Pets schema.
type Pet struct {
config `json:"-"`
// ID of the ent.
ID uint32 `json:"id,omitempty"`
// Sex holds the value of the "sex" field.
Sex uint32 `json:"sex,omitempty"`
selectValues sql.SelectValues
}
ID
, Sex
のフィールドにjson:"omitempty"
が勝手に付与されているのがわかるかと思います。
json:"omitempty"
が勝手に付与されていることにより、
例えばWeb APIのエンドポイントを作成し、Pet
のインスタンスをJSONレスポンスとして返却しようとすると、
{
"pets": [
{
"id": 1,
"sex": 1
},
{
"id": 2
}
]
}
のように、場合によってJSONからフィールド自体が消えてしまい、クライアント側の実装次第では困ったことになってしまいます。
これを回避しようとすると、全てのフィールド定義でStructTag(
のように手動でJSONタグを定義するしか無いのですが、面倒臭い上に人間がコードを書く以上定義漏れを完全に防ぐことが難しい状況でした。json:"id"
)
解決
以下のようにJSONタグを書き換える関数を作成し、その関数で変換したフィールドを返すようにすることで解決しました。
package schema
// ...
// Fields of the Pets.
func (Pets) Fields() []ent.Field {
- return []ent.Field{
+ return WrapFields(
field.Uint32("id"),
field.Uint32("sex"),
- }
+ )
}
ent/schema/wrapfields.go
package schema
import (
"fmt"
"entgo.io/ent"
"github.com/a10adotapp/a10a.app/lib/slices"
)
func WrapFields(fields ...ent.Field) []ent.Field {
return slices.Map(fields, make([]ent.Field, 0), func(field ent.Field) ent.Field {
field.Descriptor().Tag = fmt.Sprintf("json:\"%s\"", field.Descriptor().Name)
return field
})
}
また、ent.を使用しているプロジェクト全てに関数を作成するのも大変だったので、ライブラリを作成しました。
これを引っ張ってきてスキーマのフィールド定義部分をラップしてしまえばjson:"omitempty"
の自動付与から脱却することができます。
おわり
同じようなことで困っていたら使ってみてください。