reffer issue <https://github.com/golang/go/issues/46065> ## Sometimes we have to deal with JSON that with unknown data types. Such as [sample JSON](https://github.com/vipally/glab/blob/master/lab27/json_test.go#L9) below:
``` [ { "kind":"dog", "attr":{ "type":"Collie", "color":"black" } }, { "kind":"duck", "attr":{ "weight":1.2 } } ] ``` The attr field maybe [below Go types](https://github.com/vipally/glab/blob/master/lab27/json_test.go#L27), which is not a certain Go type that is decided on the value of field "kind". ```Go type DogAttr struct { Type string `json:"type"` Color string `json:"color"` } type DuckAttr struct { Weight float64 } ``` Currently, we may deal with this case as [below](https://github.com/vipally/glab/blob/master/lab27/json_raw_test.go#L9): ```Go func TestDecodeRaw(t *testing.T) { var factory = NewFactory() factory.MustReg("dog", (*DogAttr)(nil)) factory.MustReg("duck", (*DuckAttr)(nil)) type AnimalRaw struct { Kind string `json:"kind"` Attr json.RawMessage `json:"attr"` } var animals []AnimalRaw json.Unmarshal(sampleJson, &animals) for i, v := range animals { d, _ := factory.Create(v.Kind) json.Unmarshal(v.Attr, d) fmt.Printf("index %d, kind=%s attr=%#v\n", i, v.Kind, d) } // Output: // index 0, kind=dog attr=&lab27.DogAttr{Type:"Collie", Color:"black"} // index 1, kind=duck attr=&lab27.DuckAttr{Weight:1.2} } ``` But the way of [generate a sample JSON](https://github.com/vipally/glab/blob/master/lab27/json_raw_test.go#L38) in this case looks ugly: ```Go func TestEncodeRaw(t *testing.T) { type AnimalRaw struct { Kind string `json:"kind"` Attr json.RawMessage `json:"attr"` } var animals = []AnimalRaw{ AnimalRaw{ Kind: "dog", Attr: []byte(`{"type": "Collie","color": "white"}`), // ugly }, AnimalRaw{ Kind: "duck", Attr: []byte(`{"Weight": 2.34}`), // ugly }, } b, _ := json.MarshalIndent(animals, "", " ") fmt.Println(string(b)) // Output: // [ // { // "kind": "dog", // "attr": { // "type": "Collie", // "color": "white" // } // }, // { // "kind": "duck", // "attr": { // "Weight": 2.34 // } // } // ] } ``` A [eleganter solution](https://github.com/vipally/go/blob/ally_feat_json_flexobj/src/encoding/json/stream.go#L285) 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". ```Go // FlexObject is an object that can encoding/decoding JSON between flex Go types. // It implements Marshaler and Unmarshaler and can delay JSON decoding // from field Raw and can direct encoding from field D. type FlexObject struct { Raw []byte // raw bytes for delay JSON decoding D interface{} // flex object for JSON encoding } // MarshalJSON encoding field D as JSON. func (f FlexObject) MarshalJSON() ([]byte, error) { return Marshal(f.D) } // UnmarshalJSON copy data into field Raw. func (f *FlexObject) UnmarshalJSON(data []byte) error { f.Raw = append(f.Raw[0:0], data...) return nil } ``` Coordinate with the [flex object factory](https://github.com/vipally/glab/blob/master/lab27/json_factory.go#L50). [The way to deal with this case](https://github.com/vipally/glab/blob/master/lab27/json_test.go#L36) maybe as below: ```Go func TestFlexObjectFactory(t *testing.T) { var factory = NewFactory() factory.MustReg("dog", (*DogAttr)(nil)) factory.MustReg("duck", (*DuckAttr)(nil)) type Animal struct { Kind string `json:"kind"` Attr json.FlexObject `json:"attr"` } var animals []Animal json.Unmarshal(sampleJson, &animals) for i, v := range animals { factory.UnmarshalJSONForFlexObj(v.Kind, &v.Attr) 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} } ``` And the way to [generate the sample JSON](https://github.com/vipally/glab/blob/master/lab27/json_test.go#L56) maybe as below: ```Go func TestGenerateJsonByFlexObject(t *testing.T) { type Animal struct { Kind string `json:"kind"` Attr json.FlexObject `json:"attr"` } var animals = []Animal{ Animal{ Kind: "dog", Attr: json.FlexObject{ D: DogAttr{ Type: "Collie", Color: "white", }, }, }, Animal{ Kind: "duck", Attr: json.FlexObject{ D: DuckAttr{ Weight: 2.34, }, }, }, } b, _ := json.MarshalIndent(animals, "", " ") fmt.Println(string(b)) // Ooutput: // [ // { // "kind": "dog", // "attr": { // "type": "Collie", // "color": "white" // } // }, // { // "kind": "duck", // "attr": { // "Weight": 2.34 // } // } // ] } ``` **As above shows, [json.FlexObject](https://github.com/vipally/go/blob/ally_feat_json_flexobj/src/encoding/json/stream.go#L285) coordinate with [json.Factory](https://github.com/vipally/glab/blob/master/lab27/json_factory.go#L50) makes the dealing with JSON flex object case eleganter and automatically.** **If the proposal is accepted, it will be my pleasure to push the PR.** -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/4d00f201-a6ec-475a-bcde-9b196d613305n%40googlegroups.com.