Skip to content

Commit

Permalink
Merge pull request #145 from paulmach/json-null
Browse files Browse the repository at this point in the history
geojson: `null` json into non-pointer Feature/FeatureCollection will set them to empty
  • Loading branch information
paulmach authored Jan 29, 2024
2 parents 8c80c61 + 6e47a37 commit 12b0e24
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 0 deletions.
6 changes: 6 additions & 0 deletions geojson/feature.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package geojson

import (
"bytes"
"fmt"

"github.com/paulmach/orb"
Expand Down Expand Up @@ -79,6 +80,11 @@ func UnmarshalFeature(data []byte) (*Feature, error) {
// UnmarshalJSON handles the correct unmarshalling of the data
// into the orb.Geometry types.
func (f *Feature) UnmarshalJSON(data []byte) error {
if bytes.Equal(data, []byte(`null`)) {
*f = Feature{}
return nil
}

doc := &featureDoc{}
err := unmarshalJSON(data, &doc)
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions geojson/feature_collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ json.Unmarshaler interfaces as well as helper functions such as
package geojson

import (
"bytes"
"fmt"

"go.mongodb.org/mongo-driver/bson"
Expand Down Expand Up @@ -85,6 +86,11 @@ func newFeatureCollectionDoc(fc FeatureCollection) map[string]interface{} {
// UnmarshalJSON decodes the data into a GeoJSON feature collection.
// Extra/foreign members will be put into the `ExtraMembers` attribute.
func (fc *FeatureCollection) UnmarshalJSON(data []byte) error {
if bytes.Equal(data, []byte(`null`)) {
*fc = FeatureCollection{}
return nil
}

tmp := make(map[string]nocopyRawMessage, 4)

err := unmarshalJSON(data, &tmp)
Expand Down
34 changes: 34 additions & 0 deletions geojson/feature_collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,40 @@ func TestFeatureCollectionMarshalJSON(t *testing.T) {
}
}

func TestFeatureCollectionMarshalJSON_null(t *testing.T) {
t.Run("pointer", func(t *testing.T) {
type S struct {
GeoJSON *FeatureCollection `json:"geojson"`
}

var s S
err := json.Unmarshal([]byte(`{"geojson": null}`), &s)
if err != nil {
t.Fatalf("unmarshal error: %v", err)
}

if s.GeoJSON != nil {
t.Errorf("should be nil, got: %v", s)
}
})

t.Run("non-pointer", func(t *testing.T) {
type S struct {
GeoJSON FeatureCollection `json:"geojson"`
}

var s S
err := json.Unmarshal([]byte(`{"geojson": null}`), &s)
if err != nil {
t.Fatalf("unmarshal error: %v", err)
}

if !reflect.DeepEqual(s.GeoJSON, FeatureCollection{}) {
t.Errorf("should be empty, got: %v", s)
}
})
}

func TestFeatureCollectionMarshal(t *testing.T) {
fc := NewFeatureCollection()
fc.Features = nil
Expand Down
35 changes: 35 additions & 0 deletions geojson/feature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"io/ioutil"
"reflect"
"strings"
"testing"

Expand Down Expand Up @@ -201,6 +202,40 @@ func TestUnmarshalFeature_missingGeometry(t *testing.T) {
})
}

func TestFeatureMarshalJSON_null(t *testing.T) {
t.Run("pointer", func(t *testing.T) {
type S struct {
GeoJSON *Feature `json:"geojson"`
}

var s S
err := json.Unmarshal([]byte(`{"geojson": null}`), &s)
if err != nil {
t.Fatalf("unmarshal error: %v", err)
}

if s.GeoJSON != nil {
t.Errorf("should be nil, got: %v", s)
}
})

t.Run("non-pointer", func(t *testing.T) {
type S struct {
GeoJSON Feature `json:"geojson"`
}

var s S
err := json.Unmarshal([]byte(`{"geojson": null}`), &s)
if err != nil {
t.Fatalf("unmarshal error: %v", err)
}

if !reflect.DeepEqual(s.GeoJSON, Feature{}) {
t.Errorf("should be empty, got: %v", s)
}
})
}

func TestUnmarshalBSON_missingGeometry(t *testing.T) {
t.Run("missing geometry", func(t *testing.T) {
f := NewFeature(nil)
Expand Down
34 changes: 34 additions & 0 deletions geojson/geometry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,40 @@ func TestGeometryUnmarshal_errors(t *testing.T) {
}
}

func TestGeometryMarshalJSON_null(t *testing.T) {
t.Run("pointer", func(t *testing.T) {
type S struct {
GeoJSON *Geometry `json:"geojson"`
}

var s S
err := json.Unmarshal([]byte(`{"geojson": null}`), &s)
if err != nil {
t.Fatalf("unmarshal error: %v", err)
}

if s.GeoJSON != nil {
t.Errorf("should be nil, got: %v", s)
}
})

t.Run("feature with null geometry", func(t *testing.T) {
type S struct {
GeoJSON *Feature `json:"geojson"`
}

var s S
err := json.Unmarshal([]byte(`{"geojson": {"type":"Feature","geometry":null,"properties":null}}`), &s)
if err != nil {
t.Fatalf("unmarshal error: %v", err)
}

if s.GeoJSON.Geometry != nil {
t.Errorf("should be nil, got: %v", s)
}
})
}

func TestHelperTypes(t *testing.T) {
// This test makes sure the marshal-unmarshal loop does the same thing.
// The code and types here are complicated to avoid duplicate code.
Expand Down

0 comments on commit 12b0e24

Please sign in to comment.