forked from mirror/go-json
add draft design
This commit is contained in:
parent
9903d614e3
commit
e09e0ff5b3
|
@ -0,0 +1,89 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/goccy/go-json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID int64
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
Address UserAddressResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserAddress struct {
|
||||||
|
UserID int64
|
||||||
|
PostCode string
|
||||||
|
City string
|
||||||
|
Address1 string
|
||||||
|
Address2 string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserAddressResolver func() (*UserAddress, error)
|
||||||
|
|
||||||
|
func (resolver UserAddressResolver) MarshalJSON() ([]byte, error) {
|
||||||
|
address, err := resolver()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.Marshal(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (resolver UserAddressResolver) ResolveQueryJSON(q *json.Query) (interface{}, error) {
|
||||||
|
// validate or rewrite json.Query
|
||||||
|
//
|
||||||
|
address, err := resolver()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return address, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserRepository struct{}
|
||||||
|
|
||||||
|
func (r UserRepository) FindByID(id int64) (*User, error) {
|
||||||
|
v := User{ID: id, Name: "Ken", Age: 20}
|
||||||
|
// resolve relation from User to UserAddress
|
||||||
|
uaRepo := new(UserAddressRepository)
|
||||||
|
v.Address = func() (*UserAddress, error) {
|
||||||
|
return uaRepo.FindByUserID(v.ID)
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserAddressRepository struct{}
|
||||||
|
|
||||||
|
func (r UserAddressRepository) FindByUserID(id int64) (*UserAddress, error) {
|
||||||
|
return &UserAddress{UserID: id, City: "A", Address1: "hoge", Address2: "fuga"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
user, err := new(UserRepository).FindByID(1)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
b, err := json.Marshal(user)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(b))
|
||||||
|
}
|
||||||
|
{
|
||||||
|
//json.QueryFromJSON(`["Name", "Age", { "Address": [ "City" ] }]`)
|
||||||
|
q := json.NewQuery().Fields(
|
||||||
|
"Name",
|
||||||
|
"Age",
|
||||||
|
json.NewQuery("Address").Fields(
|
||||||
|
"City",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
b, err := json.MarshalWithQuery(user, q)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(b))
|
||||||
|
}
|
||||||
|
}
|
21
encode.go
21
encode.go
|
@ -148,6 +148,27 @@ func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
|
||||||
return copied, nil
|
return copied, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func marshalQuery(v interface{}, q *Query) ([]byte, error) {
|
||||||
|
ctx := takeEncodeRuntimeContext()
|
||||||
|
|
||||||
|
buf, err := encode(ctx, v, EncodeOptionHTMLEscape)
|
||||||
|
if err != nil {
|
||||||
|
releaseEncodeRuntimeContext(ctx)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// this line exists to escape call of `runtime.makeslicecopy` .
|
||||||
|
// if use `make([]byte, len(buf)-1)` and `copy(copied, buf)`,
|
||||||
|
// dst buffer size and src buffer size are differrent.
|
||||||
|
// in this case, compiler uses `runtime.makeslicecopy`, but it is slow.
|
||||||
|
buf = buf[:len(buf)-1]
|
||||||
|
copied := make([]byte, len(buf))
|
||||||
|
copy(copied, buf)
|
||||||
|
|
||||||
|
releaseEncodeRuntimeContext(ctx)
|
||||||
|
return copied, nil
|
||||||
|
}
|
||||||
|
|
||||||
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
|
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
|
||||||
ctx := takeEncodeRuntimeContext()
|
ctx := takeEncodeRuntimeContext()
|
||||||
|
|
||||||
|
|
8
json.go
8
json.go
|
@ -13,6 +13,10 @@ type Marshaler interface {
|
||||||
MarshalJSON() ([]byte, error)
|
MarshalJSON() ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueryResolver interface {
|
||||||
|
ResolveQueryJSON(*Query) (interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
// Unmarshaler is the interface implemented by types
|
// Unmarshaler is the interface implemented by types
|
||||||
// that can unmarshal a JSON description of themselves.
|
// that can unmarshal a JSON description of themselves.
|
||||||
// The input can be assumed to be a valid encoding of
|
// The input can be assumed to be a valid encoding of
|
||||||
|
@ -172,6 +176,10 @@ func MarshalWithOption(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, err
|
||||||
return marshal(v, opt)
|
return marshal(v, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MarshalWithQuery(v interface{}, q *Query) ([]byte, error) {
|
||||||
|
return marshalQuery(v, q)
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalIndent is like Marshal but applies Indent to format the output.
|
// MarshalIndent is like Marshal but applies Indent to format the output.
|
||||||
// Each JSON element in the output will begin on a new line beginning with prefix
|
// Each JSON element in the output will begin on a new line beginning with prefix
|
||||||
// followed by one or more copies of indent according to the indentation nesting.
|
// followed by one or more copies of indent according to the indentation nesting.
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package json
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Query struct {
|
||||||
|
query *subQuery
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Query) String() string {
|
||||||
|
if q.err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if q.query == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
b, err := Marshal(q.query.dump())
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Query) Error() error {
|
||||||
|
return q.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Query) Fields(fieldNameOrQueryList ...interface{}) *Query {
|
||||||
|
for _, fieldNameOrQuery := range fieldNameOrQueryList {
|
||||||
|
switch v := fieldNameOrQuery.(type) {
|
||||||
|
case string:
|
||||||
|
q.fields = append(q.fields, &subQuery{name: v})
|
||||||
|
case *Query:
|
||||||
|
q.fields = append(q.fields, v.query)
|
||||||
|
q.err = v.err
|
||||||
|
default:
|
||||||
|
q.err = fmt.Errorf("children types must be string or *Query but found %T", fieldNameOrQuery)
|
||||||
|
}
|
||||||
|
if q.err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQuery(name ...string) *Query {
|
||||||
|
if len(name) > 1 {
|
||||||
|
return &Query{err: fmt.Errorf(
|
||||||
|
"NewQuery's argument allow empty or single name only, but passed %v", name,
|
||||||
|
)}
|
||||||
|
}
|
||||||
|
return &Query{query: &subQuery{name: name}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type subQuery struct {
|
||||||
|
name string
|
||||||
|
fields []*subQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *subQuery) dump() interface{} {
|
||||||
|
fields := []interface{}{}
|
||||||
|
for _, field := range q.fields {
|
||||||
|
fields = append(fields, field.dump())
|
||||||
|
}
|
||||||
|
if q.name != "" {
|
||||||
|
return map[string][]interface{}{
|
||||||
|
q.name: fields,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return interface{}(fields)
|
||||||
|
}
|
Loading…
Reference in New Issue