go - 将 JSONSchema 解析为 golang 中的结构类型
问题描述
因此,我的用例包括将不同的 JSON 模式解析为新的结构类型,这将进一步与 ORM 一起用于从 SQL 数据库中获取数据。由于本质上是编译的,我相信在 go 中不会有一个开箱即用的解决方案,但是是否有任何 hack 可以做到这一点,而无需创建单独的 go 进程。我尝试了反思,但找不到令人满意的方法。
目前,我正在使用生成结构的ah generate库,但我被困在如何在运行时加载这些新的结构类型。
编辑
示例 JSON 架构:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Address",
"id": "Address",
"type": "object",
"description": "address",
"properties": {
"houseName": {
"type": "string",
"description": "House Name",
"maxLength": 30
},
"houseNumber": {
"type": "string",
"description": "House Number",
"maxLength": 4
},
"flatNumber": {
"type": "string",
"description": "Flat",
"maxLength": 15
},
"street": {
"type": "string",
"description": "Address 1",
"maxLength": 40
},
"district": {
"type": "string",
"description": "Address 2",
"maxLength": 30
},
"town": {
"type": "string",
"description": "City",
"maxLength": 20
},
"county": {
"type": "string",
"description": "County",
"maxLength": 20
},
"postcode": {
"type": "string",
"description": "Postcode",
"maxLength": 8
}
}
}
现在,在上述库中,有一个命令行工具,它为上述 json 生成结构类型的文本,如下所示:
// Code generated by schema-generate. DO NOT EDIT.
package main
// Address address
type Address struct {
County string `json:"county,omitempty"`
District string `json:"district,omitempty"`
FlatNumber string `json:"flatNumber,omitempty"`
HouseName string `json:"houseName,omitempty"`
HouseNumber string `json:"houseNumber,omitempty"`
Postcode string `json:"postcode,omitempty"`
Street string `json:"street,omitempty"`
Town string `json:"town,omitempty"`
}
现在,问题是如何在不重新编译程序的情况下使用这种结构类型。有一个 hack,我可以在其中开始一个新的 go 进程,但这似乎不是一个好方法。另一种方法是编写我自己的解析器来解组 JSON 模式,例如:
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
var f interface{}
json.Unmarshal(b, &f)
m := f.(map[string]interface{})
for k, v := range m {
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case float64:
fmt.Println(k, "is float64", vv)
case int:
fmt.Println(k, "is int", vv)
case []interface{}:
fmt.Println(k, "is an array:")
for i, u := range vv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "is of a type I don't know how to handle")
}
}
有人可以建议一些要寻找的指针。谢谢。
解决方案
所以看起来你正在尝试实现自己的 json 编组。这没什么大不了的:标准的 json 包已经支持它。只需让您的类型实现MarshalJSON
和UnmarshalJSON
功能(参见文档上的第一个示例)。假设某些字段将被共享(例如 schema、id、type),您可以像这样创建一个统一的类型:
// poor naming, but we need this level of wrapping here
type Data struct {
Metadata
}
type Metadata struct {
Schema string `json:"$schema"`
Type string `json:"type"`
Description string `json:"description"`
Id string `json:"id"`
Properties json.RawMessage `json:"properties"`
Address *Address `json:"-"`
// other types go here, too
}
现在所有属性都将被解组到一个json.RawMessage
字段中(本质上这是一个[]byte
字段)。您现在可以在自定义 unmarshall 函数中执行以下操作:
func (d *Data) UnmarshalJSON(b []byte) error {
meta := Metadata{}
// unmarshall common fields
if err := json.Unmarshal(b, &meta); err != nil {
return err
}
// Assuming the Type field contains the value that allows you to determine what data you're actually unmarshalling
switch meta.Type {
case "address":
meta.Address = &Address{} // initialise field
if err := json.Unmarshal([]byte(meta.Properties), meta.Address); err != nil {
return err
}
case "name":
meta.Name = &Name{}
if err := json.Unmarshal([]byte(meta.Properties), meta.Name); err != nil {
return err
}
default:
return errors.New("unknown message type")
}
// all done
d.Metadata = meta // assign to embedded
// optionally: clean up the Properties field, as it contains raw JSON, and is exported
d.Metadata.Properties = json.RawMessage{}
return nil
}
您可以对编组执行几乎相同的操作。首先确定您实际使用的类型,然后将该对象编组到属性字段中,然后编组整个结构
func (d Data) MarshalJSON() ([]byte, error) {
var (
prop []byte
err error
)
switch {
case d.Metadata.Address != nil:
prop, err = json.Marshal(d.Address)
case d.Metadata.Name != nil:
prop, err = json.Marshal(d.Name) // will only work if field isn't masked, better to be explicit
default:
err = errors.New("No properties to marshal") // handle in whatever way is best
}
if err != nil {
return nil, err
}
d.Metadata.Properties = json.RawMessage(prop)
return json.Marshal(d.Metadata) // marshal the unified type here
}
推荐阅读
- mysql - 将 docker mysql 卷从容器转换为共享目录
- mysql - 我无法运行 mysql 命令行
- couchdb - writing a map function in couchdb db that inserts another doc into a doc
- r - 从R中的图像修改像素颜色
- ios - 有没有办法让 ios 的通知监听器在颤动
- python - 如何将python数组分配给gnuplot数组?
- javascript - material-ui table 多子行不能一起悬停
- css - ICSS :export 函数将所有键转换为 camelCase
- node.js - 在nodejs中处理非常大的文件
- sql - 我错过了什么sql公式?