Added JSON support for NullDecimal (#62)

* Corrected NullDecimal comment

Saying that NullDecimal is immutable is just incorrect, and we don't need
to duplicate the description of properties of a Decimal type here.

* Added UnmarshalJSON and MarshalJSON support for NullDecimal

* Added tests for JSON operations on NullDecimal

* Fixed mistakes in tests
This commit is contained in:
MOZGIII 2017-10-11 01:21:51 +09:00 committed by Victor Quinn
parent aed1bfe463
commit ad668bb369
2 changed files with 112 additions and 2 deletions

View file

@ -928,8 +928,8 @@ func unquoteIfQuoted(value interface{}) (string, error) {
return string(bytes), nil return string(bytes), nil
} }
// NullDecimal represents a fixed-point decimal. It is immutable. // NullDecimal represents a nullable decimal with compatibility for
// number = value * 10 ^ exp // scanning null values from the database.
type NullDecimal struct { type NullDecimal struct {
Decimal Decimal Decimal Decimal
Valid bool Valid bool
@ -952,3 +952,21 @@ func (d NullDecimal) Value() (driver.Value, error) {
} }
return d.Decimal.Value() return d.Decimal.Value()
} }
// UnmarshalJSON implements the json.Unmarshaler interface.
func (d *NullDecimal) UnmarshalJSON(decimalBytes []byte) error {
if string(decimalBytes) == "null" {
d.Valid = false
return nil
}
d.Valid = true
return d.Decimal.UnmarshalJSON(decimalBytes)
}
// MarshalJSON implements the json.Marshaler interface.
func (d NullDecimal) MarshalJSON() ([]byte, error) {
if !d.Valid {
return []byte("null"), nil
}
return d.Decimal.MarshalJSON()
}

View file

@ -332,6 +332,98 @@ func TestBadJSON(t *testing.T) {
} }
} }
func TestNullDecimalJSON(t *testing.T) {
for _, s := range testTable {
var doc struct {
Amount NullDecimal `json:"amount"`
}
docStr := `{"amount":"` + s + `"}`
docStrNumber := `{"amount":` + s + `}`
err := json.Unmarshal([]byte(docStr), &doc)
if err != nil {
t.Errorf("error unmarshaling %s: %v", docStr, err)
} else {
if !doc.Amount.Valid {
t.Errorf("expected %s to be valid (not NULL), got Valid = false", s)
}
if doc.Amount.Decimal.String() != s {
t.Errorf("expected %s, got %s (%s, %d)",
s, doc.Amount.Decimal.String(),
doc.Amount.Decimal.value.String(), doc.Amount.Decimal.exp)
}
}
out, err := json.Marshal(&doc)
if err != nil {
t.Errorf("error marshaling %+v: %v", doc, err)
} else if string(out) != docStr {
t.Errorf("expected %s, got %s", docStr, string(out))
}
// make sure unquoted marshalling works too
MarshalJSONWithoutQuotes = true
out, err = json.Marshal(&doc)
if err != nil {
t.Errorf("error marshaling %+v: %v", doc, err)
} else if string(out) != docStrNumber {
t.Errorf("expected %s, got %s", docStrNumber, string(out))
}
MarshalJSONWithoutQuotes = false
}
var doc struct {
Amount NullDecimal `json:"amount"`
}
docStr := `{"amount": null}`
err := json.Unmarshal([]byte(docStr), &doc)
if err != nil {
t.Errorf("error unmarshaling %s: %v", docStr, err)
} else if doc.Amount.Valid {
t.Errorf("expected null value to have Valid = false, got Valid = true and Decimal = %s (%s, %d)",
doc.Amount.Decimal.String(),
doc.Amount.Decimal.value.String(), doc.Amount.Decimal.exp)
}
expected := `{"amount":null}`
out, err := json.Marshal(&doc)
if err != nil {
t.Errorf("error marshaling %+v: %v", doc, err)
} else if string(out) != expected {
t.Errorf("expected %s, got %s", expected, string(out))
}
// make sure unquoted marshalling works too
MarshalJSONWithoutQuotes = true
expectedUnquoted := `{"amount":null}`
out, err = json.Marshal(&doc)
if err != nil {
t.Errorf("error marshaling %+v: %v", doc, err)
} else if string(out) != expectedUnquoted {
t.Errorf("expected %s, got %s", expectedUnquoted, string(out))
}
MarshalJSONWithoutQuotes = false
}
func TestNullDecimalBadJSON(t *testing.T) {
for _, testCase := range []string{
"]o_o[",
"{",
`{"amount":""`,
`{"amount":""}`,
`{"amount":"nope"}`,
`{"amount":nope}`,
`0.333`,
} {
var doc struct {
Amount NullDecimal `json:"amount"`
}
err := json.Unmarshal([]byte(testCase), &doc)
if err == nil {
t.Errorf("expected error, got %+v", doc)
}
}
}
func TestXML(t *testing.T) { func TestXML(t *testing.T) {
for _, s := range testTable { for _, s := range testTable {
var doc struct { var doc struct {