Skip to content

Instantly share code, notes, and snippets.

@narqo
Last active August 28, 2018 20:06
Show Gist options
  • Save narqo/af6d83b0ccf66e8e643aa89cb8625753 to your computer and use it in GitHub Desktop.
Save narqo/af6d83b0ccf66e8e643aa89cb8625753 to your computer and use it in GitHub Desktop.
Rename struct's fields while keep supporting old JSON data
package callback_test
import (
"encoding/json"
"fmt"
"reflect"
"testing"
"time"
)
type Data struct {
// renamed from ClickTime
EngagementTime time.Time `json:"engagement_time,omitempty"`
// Deprecated: legacy field we want to switch from gracefully; use EngagementTime
ClickTime time.Time `json:",omitempty"`
}
func (data *Data) UnmarshalJSON(b []byte) error {
// new type to prevent recursive Data.UnmarshalJSON calls
type NoMethodsData Data
var legacyData NoMethodsData
if err := json.Unmarshal(b, &legacyData); err != nil {
return err
}
*data = Data(legacyData)
if data.EngagementTime.IsZero() {
// no new field was set, fallback to legacy field
data.EngagementTime = legacyData.ClickTime
}
return nil
}
func TestData_legacyFields(t *testing.T) {
t1 := time.Now().Truncate(time.Second)
v1, _ := t1.MarshalText()
cases := []struct {
json string
data Data
}{
{
fmt.Sprintf(`{"engagement_time": "%s"}`, v1),
Data{
EngagementTime: t1,
ClickTime: time.Time{},
},
},
{
fmt.Sprintf(`{"ClickTime": "%s"}`, v1),
Data{
EngagementTime: t1,
ClickTime: t1,
},
},
}
for n, tc := range cases {
var data Data
if err := json.Unmarshal([]byte(tc.json), &data); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(tc.data, data) {
t.Errorf("case %d: want %+v, got %+v", n, tc.data, data)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment