-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: encoding/json: add FlexObject for encoding/decoding between JSON and flex Go types. #46065
Comments
You don't have to use |
So please unmarshal sample JSON into proper type of xxAttr: https://play.golang.org/p/FxvATK57w14 |
Sorry if my response came across as too direct, I was just providing an example that I believe solves the problem without adding anything to stdlib. For umarshalling, |
Do you notice that you use the same idea with FlexObject: json.RawMessage=>interface{} to delay JSON decoding into uncertain Go types, and interface{} to direct encoding to JSON. |
If the |
// for JSON encoding only
type AnimalEncode struct {
Kind string `json:"kind"`
Attr interface{} `json:"attr"` // for JSON encoding only
}
// for JSON decoding only
type AnimalDecode struct {
Kind string `json:"kind"`
Attr json.RawMessage `json:"attr"` // for JSON decoding only
}
|
"Common" is a fairly subjective adjective and a lot of decision for proposals are driven by hard data. I believe the burden of proof is on the proposer to show that such a proposed pattern is sufficiently widely used. Can you provide data that this is the case? |
I should also note that this can also be solved with #5901. Instead of a For unmarshaling, you can register custom unmarshaler that unmarshals into any dec.Register(func(b []byte, v *interface{}) error {
*v = append(json.RawMessage(nil), b...)
return nil
}) |
It sounds like this can be done with the existing API. |
This proposal has been added to the active column of the proposals project |
cc @mvdan |
Based on the discussion above, this proposal seems like a likely decline. |
Maybe I have missing something. I'am interested in how will gophers solve this case based on #5901 var sampleJson = []byte(`
[
{
"kind":"dog",
"attr":{
"type":"Collie",
"color":"black"
}
},
{
"kind":"duck",
"attr":{
"weight":1.2
}
}
]
`)
func TestCase5901(t *testing.T) {
type AnimalRaw struct {
Kind string `json:"kind"`
Attr interface{} `json:"attr"`
}
var animals []AnimalRaw
var dec = json.NewDecoder(bytes.NewReader(sampleJson))
dec.Register(func(b []byte, v *interface{}) error {
*v = append(json.RawMessage(nil), b...)
return nil
})
dec.Decode(&animals)
fmt.Printf("%#v\n", animals)
} And how about the behavior of the defalut API? func TestCase5901_default(t *testing.T) {
type AnimalRaw struct {
Kind string `json:"kind"`
Attr interface{} `json:"attr"`
}
var animals []AnimalRaw
json.Unmarshal(sampleJson, &animals) // the behavior of Attr?
fmt.Printf("%#v\n", animals)
} And the behavior of 3rd-party APIs? import "github.com/gin-gonic/gin"
func TestCase5901_3rdParty(t *testing.T) {
type AnimalRaw struct {
Kind string `json:"kind"`
Attr interface{} `json:"attr"`
}
var animals []AnimalRaw
gin.Context.BindJSON(&animals) // the behavior of Attr?
fmt.Printf("%#v\n", animals)
}
//---------git.luolix.top/gin-gonic/gin.BindJson-------------------------
func decodeJSON(r io.Reader, obj interface{}) error {
decoder := json.NewDecoder(r)
if EnableDecoderUseNumber {
decoder.UseNumber()
}
if EnableDecoderDisallowUnknownFields {
decoder.DisallowUnknownFields()
}
if err := decoder.Decode(obj); err != nil {
return err
}
return validate(obj)
} So @dsnet could you show more examples on how gophers will solve this case based on #5901 ? |
I'm not sure I follow. The example shown with |
Well, the basic demand of this case is: As above shows,
So I think #5901 can't meet this demand unless request below proposal: |
According to discussion above, update the implementation of FlexObject: // FlexObject is an object that can encoding/decoding JSON between flex Go types.
// It implements Marshaler and Unmarshaler and can delay JSON decoding
// from []byte into flex object.
type FlexObject struct {
D interface{} // flex object for JSON encoding and decoding
}
// MarshalJSON encoding field D as JSON.
func (f FlexObject) MarshalJSON() ([]byte, error) {
return Marshal(f.D)
}
// UnmarshalJSON copy data into field D.
func (f *FlexObject) UnmarshalJSON(data []byte) error {
f.D = append([]byte(nil), data...)
return nil
}
// DelayedUnmarshalJSON unmarshal []byte into instance d.
// It will update field D if unmarshal OK.
func (f *FlexObject) DelayedUnmarshalJSON(d interface{}) error {
b, ok := f.D.([]byte)
if !ok {
return errors.New("json.FlexObject: DelayedUnmarshalJSON on none []byte value")
}
if err := Unmarshal(b, d); err != nil {
return err
}
f.D = d
return nil
} And the example of decoding code: func TestDecodeFlexObject(t *testing.T) {
type Animal struct {
Kind string `json:"kind"`
Attr json.FlexObject `json:"attr"`
}
var animals []Animal
json.Unmarshal(sampleJson, &animals)
for i, v := range animals {
var d interface{}
switch v.Kind {
case "dog":
d = &DogAttr{}
case "duck":
d = &DuckAttr{}
default:
t.Fatalf("unsupport kind %s", v.Kind)
}
if err := v.Attr.DelayedUnmarshalJSON(d); err != nil { // delay decoding FlexObject
t.Fatal(err)
}
fmt.Printf("index %d, kind=%s attr=%#v\n", i, v.Kind, v.Attr.D)
}
// Output:
// index 0, kind=dog attr=&lab27.DogAttr{Type:"Collie", Color:"black"}
// index 1, kind=duck attr=&lab27.DuckAttr{Weight:1.2}
} |
@vipally, I mentioned as #5901 as one possible way to achieve what this issue seems to trying to do. I never claimed it was the only way nor perfect way (since it can only be done at the Given that there are several ways to accomplish this outside the standard
Judging by the reaction by most others on this issue, it seems that this feature does not seem sufficiently useful to be included. |
I assume that you have notice that #5901 can't meet this demand.
I'm looking forward to other solutions could achive this perfectly, but at least so far, nobody gives an example.
I can't list all use cases.
|
No change in consensus, so declined. |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes
Sometimes we have to deal with JSON that with unknown data types.
Such as sample JSON below:
The attr field maybe below Go types, which is not a certain Go type that is decided on the value of field "kind".
Currently, we may deal with this case as below:
But the way of generate a sample JSON in this case looks ugly:
An eleganter solution of this case maybe as below. Compare with json.RawMessage, FlexObject can delay JSON decoding
from field "Raw" into field "D" and can direct encoding JSON from field "D".
Coordinate with the flex object factory. The way to deal with this case maybe as below:
And the way to generate the sample JSON maybe as below:
As above shows, json.FlexObject coordinate with json.Factory makes the dealing with JSON flex object case eleganter and automatically.
Actually json.FlexObject is designed to replace json.RawMessage.But if remove json.RawMessage directly, it will break the consensus of Go 1. Maybe json.RawMessage can be removed from Go 2.
If the proposal is accepted, it will be my pleasure to push the PR.
The text was updated successfully, but these errors were encountered: