Fix proto3 JSON (#6345)
* Fix jsonpb * linting * cleanup Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
This commit is contained in:
parent
e248693ac8
commit
1e6953ccdf
|
@ -4,6 +4,8 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
|
||||
"github.com/gogo/protobuf/jsonpb"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
)
|
||||
|
@ -25,6 +27,11 @@ func MarshalIndentFromJSON(bz []byte) ([]byte, error) {
|
|||
// bytes of a message.
|
||||
func ProtoMarshalJSON(msg proto.Message) ([]byte, error) {
|
||||
jm := &jsonpb.Marshaler{EmitDefaults: false, OrigName: false}
|
||||
err := types.UnpackInterfaces(msg, types.ProtoJSONPacker{JSONPBMarshaler: jm})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if err := jm.Marshal(buf, msg); err != nil {
|
||||
|
@ -38,6 +45,11 @@ func ProtoMarshalJSON(msg proto.Message) ([]byte, error) {
|
|||
// JSON encoded bytes of a message.
|
||||
func ProtoMarshalJSONIndent(msg proto.Message) ([]byte, error) {
|
||||
jm := &jsonpb.Marshaler{EmitDefaults: false, OrigName: false, Indent: " "}
|
||||
err := types.UnpackInterfaces(msg, types.ProtoJSONPacker{JSONPBMarshaler: jm})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if err := jm.Marshal(buf, msg); err != nil {
|
||||
|
|
|
@ -49,7 +49,7 @@ type Any struct {
|
|||
|
||||
cachedValue interface{}
|
||||
|
||||
aminoCompat *aminoCompat
|
||||
compat *anyCompat
|
||||
}
|
||||
|
||||
// NewAnyWithValue constructs a new Any packed with the value provided or
|
||||
|
|
|
@ -5,60 +5,61 @@ import (
|
|||
"reflect"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/gogo/protobuf/jsonpb"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
)
|
||||
|
||||
type aminoCompat struct {
|
||||
bz []byte
|
||||
jsonBz []byte
|
||||
err error
|
||||
type anyCompat struct {
|
||||
aminoBz []byte
|
||||
jsonBz []byte
|
||||
err error
|
||||
}
|
||||
|
||||
var Debug = false
|
||||
var Debug = true
|
||||
|
||||
func aminoCompatError(errType string, x interface{}) error {
|
||||
func anyCompatError(errType string, x interface{}) error {
|
||||
if Debug {
|
||||
debug.PrintStack()
|
||||
}
|
||||
return fmt.Errorf(
|
||||
"amino %s Any marshaling error for %+v, this is likely because "+
|
||||
"%s marshaling error for %+v, this is likely because "+
|
||||
"amino is being used directly (instead of codec.Codec which is preferred) "+
|
||||
"or UnpackInterfacesMessage is not defined for some type which contains "+
|
||||
"a protobuf Any either directly or via one of its members. To see a "+
|
||||
"stacktrace of where the error is coming from, set the var Debug = true "+
|
||||
"in codec/types/amino_compat.go",
|
||||
"in codec/types/compat.go",
|
||||
errType, x,
|
||||
)
|
||||
}
|
||||
|
||||
func (any Any) MarshalAmino() ([]byte, error) {
|
||||
ac := any.aminoCompat
|
||||
ac := any.compat
|
||||
if ac == nil {
|
||||
return nil, aminoCompatError("binary unmarshal", any)
|
||||
return nil, anyCompatError("amino binary unmarshal", any)
|
||||
}
|
||||
return ac.bz, ac.err
|
||||
return ac.aminoBz, ac.err
|
||||
}
|
||||
|
||||
func (any *Any) UnmarshalAmino(bz []byte) error {
|
||||
any.aminoCompat = &aminoCompat{
|
||||
bz: bz,
|
||||
err: nil,
|
||||
any.compat = &anyCompat{
|
||||
aminoBz: bz,
|
||||
err: nil,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any Any) MarshalJSON() ([]byte, error) {
|
||||
ac := any.aminoCompat
|
||||
ac := any.compat
|
||||
if ac == nil {
|
||||
return nil, aminoCompatError("JSON marshal", any)
|
||||
return nil, anyCompatError("JSON marshal", any)
|
||||
}
|
||||
return ac.jsonBz, ac.err
|
||||
}
|
||||
|
||||
func (any *Any) UnmarshalJSON(bz []byte) error {
|
||||
any.aminoCompat = &aminoCompat{
|
||||
any.compat = &anyCompat{
|
||||
jsonBz: bz,
|
||||
err: nil,
|
||||
}
|
||||
|
@ -74,11 +75,11 @@ type AminoUnpacker struct {
|
|||
var _ AnyUnpacker = AminoUnpacker{}
|
||||
|
||||
func (a AminoUnpacker) UnpackAny(any *Any, iface interface{}) error {
|
||||
ac := any.aminoCompat
|
||||
ac := any.compat
|
||||
if ac == nil {
|
||||
return aminoCompatError("binary unmarshal", reflect.TypeOf(iface))
|
||||
return anyCompatError("amino binary unmarshal", reflect.TypeOf(iface))
|
||||
}
|
||||
err := a.Cdc.UnmarshalBinaryBare(ac.bz, iface)
|
||||
err := a.Cdc.UnmarshalBinaryBare(ac.aminoBz, iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -98,7 +99,7 @@ func (a AminoUnpacker) UnpackAny(any *Any, iface interface{}) error {
|
|||
|
||||
// this is necessary for tests that use reflect.DeepEqual and compare
|
||||
// proto vs amino marshaled values
|
||||
any.aminoCompat = nil
|
||||
any.compat = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -117,9 +118,9 @@ func (a AminoPacker) UnpackAny(any *Any, _ interface{}) error {
|
|||
return err
|
||||
}
|
||||
bz, err := a.Cdc.MarshalBinaryBare(any.cachedValue)
|
||||
any.aminoCompat = &aminoCompat{
|
||||
bz: bz,
|
||||
err: err,
|
||||
any.compat = &anyCompat{
|
||||
aminoBz: bz,
|
||||
err: err,
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -133,9 +134,9 @@ type AminoJSONUnpacker struct {
|
|||
var _ AnyUnpacker = AminoJSONUnpacker{}
|
||||
|
||||
func (a AminoJSONUnpacker) UnpackAny(any *Any, iface interface{}) error {
|
||||
ac := any.aminoCompat
|
||||
ac := any.compat
|
||||
if ac == nil {
|
||||
return aminoCompatError("JSON unmarshal", reflect.TypeOf(iface))
|
||||
return anyCompatError("JSON unmarshal", reflect.TypeOf(iface))
|
||||
}
|
||||
err := a.Cdc.UnmarshalJSON(ac.jsonBz, iface)
|
||||
if err != nil {
|
||||
|
@ -157,7 +158,7 @@ func (a AminoJSONUnpacker) UnpackAny(any *Any, iface interface{}) error {
|
|||
|
||||
// this is necessary for tests that use reflect.DeepEqual and compare
|
||||
// proto vs amino marshaled values
|
||||
any.aminoCompat = nil
|
||||
any.compat = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -176,9 +177,37 @@ func (a AminoJSONPacker) UnpackAny(any *Any, _ interface{}) error {
|
|||
return err
|
||||
}
|
||||
bz, err := a.Cdc.MarshalJSON(any.cachedValue)
|
||||
any.aminoCompat = &aminoCompat{
|
||||
any.compat = &anyCompat{
|
||||
jsonBz: bz,
|
||||
err: err,
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ProtoJSONPacker is an AnyUnpacker provided for compatibility with jsonpb
|
||||
type ProtoJSONPacker struct {
|
||||
JSONPBMarshaler *jsonpb.Marshaler
|
||||
}
|
||||
|
||||
var _ AnyUnpacker = ProtoJSONPacker{}
|
||||
|
||||
func (a ProtoJSONPacker) UnpackAny(any *Any, _ interface{}) error {
|
||||
if any == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if any.cachedValue != nil {
|
||||
err := UnpackInterfaces(any.cachedValue, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
bz, err := a.JSONPBMarshaler.MarshalToString(any)
|
||||
any.compat = &anyCompat{
|
||||
jsonBz: []byte(bz),
|
||||
err: err,
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
package types_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/jsonpb"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -131,3 +134,41 @@ func TestNested(t *testing.T) {
|
|||
|
||||
require.Equal(t, spot, hhha2.TheHasHasAnimal().TheHasAnimal().TheAnimal())
|
||||
}
|
||||
|
||||
func TestAny_ProtoJSON(t *testing.T) {
|
||||
spot := &testdata.Dog{Name: "Spot"}
|
||||
any, err := types.NewAnyWithValue(spot)
|
||||
require.NoError(t, err)
|
||||
|
||||
jm := &jsonpb.Marshaler{}
|
||||
json, err := jm.MarshalToString(any)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "{\"@type\":\"/cosmos_sdk.codec.v1.Dog\",\"name\":\"Spot\"}", json)
|
||||
|
||||
registry := NewTestInterfaceRegistry()
|
||||
jum := &jsonpb.Unmarshaler{}
|
||||
var any2 types.Any
|
||||
err = jum.Unmarshal(strings.NewReader(json), &any2)
|
||||
require.NoError(t, err)
|
||||
var animal testdata.Animal
|
||||
err = registry.UnpackAny(&any2, &animal)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, spot, animal)
|
||||
|
||||
ha := &testdata.HasAnimal{
|
||||
Animal: any,
|
||||
}
|
||||
err = ha.UnpackInterfaces(types.ProtoJSONPacker{JSONPBMarshaler: jm})
|
||||
require.NoError(t, err)
|
||||
json, err = jm.MarshalToString(ha)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "{\"animal\":{\"@type\":\"/cosmos_sdk.codec.v1.Dog\",\"name\":\"Spot\"}}", json)
|
||||
|
||||
require.NoError(t, err)
|
||||
var ha2 testdata.HasAnimal
|
||||
err = jum.Unmarshal(strings.NewReader(json), &ha2)
|
||||
require.NoError(t, err)
|
||||
err = ha2.UnpackInterfaces(registry)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, spot, ha2.Animal.GetCachedValue())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue