Browse Source

add support for `json=""` in reflection binary/* thing

pull/43/merge
Jae Kwon 10 years ago
parent
commit
7b049e93fb
2 changed files with 67 additions and 19 deletions
  1. +47
    -19
      binary/reflect.go
  2. +20
    -0
      binary/reflect_test.go

+ 47
- 19
binary/reflect.go View File

@ -9,7 +9,7 @@ import (
"sync" "sync"
"time" "time"
. "github.com/tendermint/tendermint2/common"
. "github.com/tendermint/tendermint/common"
) )
type TypeInfo struct { type TypeInfo struct {
@ -26,6 +26,19 @@ type TypeInfo struct {
// If Type is concrete // If Type is concrete
HasTypeByte bool HasTypeByte bool
TypeByte byte TypeByte byte
// If Type is kind reflect.Struct
Fields []StructFieldInfo
}
type StructFieldInfo struct {
Index int // Struct field index
JSONName string // Corresponding JSON field name. (override with `json=""`)
Type reflect.Type // Struct field type
}
func (info StructFieldInfo) unpack() (int, string, reflect.Type) {
return info.Index, info.JSONName, info.Type
} }
// e.g. If o is struct{Foo}{}, return is the Foo interface type. // e.g. If o is struct{Foo}{}, return is the Foo interface type.
@ -155,6 +168,28 @@ func RegisterType(info *TypeInfo) *TypeInfo {
} }
} }
// If struct, register field name options
if rt.Kind() == reflect.Struct {
numFields := rt.NumField()
structFields := []StructFieldInfo{}
for i := 0; i < numFields; i++ {
field := rt.Field(i)
if field.PkgPath != "" {
continue
}
jsonName := field.Tag.Get("json")
if jsonName == "" {
jsonName = field.Name
}
structFields = append(structFields, StructFieldInfo{
Index: i,
JSONName: jsonName,
Type: field.Type,
})
}
info.Fields = structFields
}
return info return info
} }
@ -532,19 +567,16 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, o interface{}, err *erro
return return
} }
// TODO: ensure that all fields are set? // TODO: ensure that all fields are set?
for name, value := range oMap {
field, ok := rt.FieldByName(name)
// TODO: disallow unknown oMap fields?
for _, fieldInfo := range typeInfo.Fields {
i, jsonName, fieldType := fieldInfo.unpack()
value, ok := oMap[jsonName]
if !ok { if !ok {
*err = errors.New(Fmt("Attempt to set unknown field %v", field.Name))
*err = errors.New(Fmt("Missing field: %v", jsonName))
return return
} }
// JAE: I don't think golang reflect lets us set unexported fields, but just in case:
if field.PkgPath != "" {
*err = errors.New(Fmt("Attempt to set unexported field %v", field.Name))
return
}
fieldRv := rv.FieldByName(name)
readReflectJSON(fieldRv, field.Type, value, err)
fieldRv := rv.Field(i)
readReflectJSON(fieldRv, fieldType, value, err)
} }
} }
@ -643,21 +675,17 @@ func writeReflectJSON(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64,
WriteTo(jsonBytes, w, n, err) WriteTo(jsonBytes, w, n, err)
} else { } else {
WriteTo([]byte("{"), w, n, err) WriteTo([]byte("{"), w, n, err)
numFields := rt.NumField()
wroteField := false wroteField := false
for i := 0; i < numFields; i++ {
field := rt.Field(i)
if field.PkgPath != "" {
continue
}
for _, fieldInfo := range typeInfo.Fields {
i, jsonName, fieldType := fieldInfo.unpack()
fieldRv := rv.Field(i) fieldRv := rv.Field(i)
if wroteField { if wroteField {
WriteTo([]byte(","), w, n, err) WriteTo([]byte(","), w, n, err)
} else { } else {
wroteField = true wroteField = true
} }
WriteTo([]byte(Fmt("\"%v\":", field.Name)), w, n, err)
writeReflectJSON(fieldRv, field.Type, w, n, err)
WriteTo([]byte(Fmt("\"%v\":", jsonName)), w, n, err)
writeReflectJSON(fieldRv, fieldType, w, n, err)
} }
WriteTo([]byte("}"), w, n, err) WriteTo([]byte("}"), w, n, err)
} }


+ 20
- 0
binary/reflect_test.go View File

@ -401,3 +401,23 @@ func TestJSON(t *testing.T) {
} }
} }
//------------------------------------------------------------------------------
type Foo struct {
FieldA string `json:"fieldA"` // json field name is "fieldA"
FieldB string // json field name is "FieldB"
fieldC string // not exported, not serialized.
}
func TestJSONFieldNames(t *testing.T) {
for i := 0; i < 20; i++ { // Try to ensure deterministic success.
foo := Foo{"a", "b", "c"}
stringified := string(JSONBytes(foo))
expected := `{"fieldA":"a","FieldB":"b"}`
if stringified != expected {
t.Fatalf("JSONFieldNames error: expected %v, got %v",
expected, stringified)
}
}
}

Loading…
Cancel
Save