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.

Reply via email to