Skip to content

Commit

Permalink
add jsonutil
Browse files Browse the repository at this point in the history
  • Loading branch information
dogancanbakir committed Feb 6, 2025
1 parent 4a4cbd9 commit b8a1c85
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 0 deletions.
64 changes: 64 additions & 0 deletions json/jsonutil.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package jsonutil

import (
"encoding/json"
"errors"
)

// FilterJSON filters the input JSON based on the include and exclude fields.
// - data: the original JSON data as a byte slice.
// - includeFields: list of fields to include (if empty, includes all).
// - excludeFields: list of fields to exclude (processed after include).
func FilterJSON(data []byte, includeFields, excludeFields []string) ([]byte, error) {
var raw map[string]interface{}
if err := json.Unmarshal(data, &raw); err != nil {
return nil, errors.New("invalid JSON input")
}

filtered := make(map[string]interface{})

includeMap := make(map[string]bool)
excludeMap := make(map[string]bool)

for _, field := range includeFields {
includeMap[field] = true
}
for _, field := range excludeFields {
excludeMap[field] = true
}

if len(includeMap) > 0 {
for field := range includeMap {
if val, exists := raw[field]; exists {
filtered[field] = val
}
}
} else {
for key, val := range raw {
filtered[key] = val
}
}

for field := range excludeMap {
delete(filtered, field)
}

return json.Marshal(filtered)
}

// GetJSONFields returns all the top-level fields from the given JSON data.
// - data: the original JSON data as a byte slice.
// Returns a slice of field names or an error if the JSON is invalid.
func GetJSONFields(data []byte) ([]string, error) {
var raw map[string]interface{}
if err := json.Unmarshal(data, &raw); err != nil {
return nil, errors.New("invalid JSON input")
}

fields := make([]string, 0, len(raw))
for key := range raw {
fields = append(fields, key)
}

return fields, nil
}
128 changes: 128 additions & 0 deletions json/jsonutil_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package jsonutil

import (
"encoding/json"
"reflect"
"testing"
)

func TestFilterJSON(t *testing.T) {
testCases := []struct {
name string
input string
includeFields []string
excludeFields []string
want string
wantErr bool
}{
{
name: "include specific fields",
input: `{"name":"John","age":30,"city":"New York"}`,
includeFields: []string{"name", "age"},
excludeFields: []string{},
want: `{"age":30,"name":"John"}`,
wantErr: false,
},
{
name: "exclude specific fields",
input: `{"name":"John","age":30,"city":"New York"}`,
includeFields: []string{},
excludeFields: []string{"age"},
want: `{"city":"New York","name":"John"}`,
wantErr: false,
},
{
name: "include and exclude",
input: `{"name":"John","age":30,"city":"New York","country":"USA"}`,
includeFields: []string{"name", "age", "city"},
excludeFields: []string{"age"},
want: `{"city":"New York","name":"John"}`,
wantErr: false,
},
{
name: "invalid JSON",
input: `{"name":"John"`,
includeFields: []string{},
excludeFields: []string{},
want: "",
wantErr: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := FilterJSON([]byte(tc.input), tc.includeFields, tc.excludeFields)

if (err != nil) != tc.wantErr {
t.Errorf("FilterJSON() error = %v, wantErr %v", err, tc.wantErr)
return
}

if !tc.wantErr {
var gotMap, wantMap map[string]interface{}
json.Unmarshal(got, &gotMap)

Check failure on line 63 in json/jsonutil_test.go

View workflow job for this annotation

GitHub Actions / Lint Test

Error return value of `json.Unmarshal` is not checked (errcheck)
json.Unmarshal([]byte(tc.want), &wantMap)

Check failure on line 64 in json/jsonutil_test.go

View workflow job for this annotation

GitHub Actions / Lint Test

Error return value of `json.Unmarshal` is not checked (errcheck)

if !reflect.DeepEqual(gotMap, wantMap) {
t.Errorf("FilterJSON() = %v, want %v", string(got), tc.want)
}
}
})
}
}

func TestGetJSONFields(t *testing.T) {
testCases := []struct {
name string
input string
want []string
wantErr bool
}{
{
name: "valid JSON",
input: `{"name":"John","age":30,"city":"New York"}`,
want: []string{"name", "age", "city"},
wantErr: false,
},
{
name: "empty JSON",
input: `{}`,
want: []string{},
wantErr: false,
},
{
name: "invalid JSON",
input: `{"name":"John"`,
want: nil,
wantErr: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := GetJSONFields([]byte(tc.input))

if (err != nil) != tc.wantErr {
t.Errorf("GetJSONFields() error = %v, wantErr %v", err, tc.wantErr)
return
}

if !tc.wantErr {
// Sort both slices to ensure consistent comparison
gotMap := make(map[string]bool)
wantMap := make(map[string]bool)

for _, field := range got {
gotMap[field] = true
}
for _, field := range tc.want {
wantMap[field] = true
}

if !reflect.DeepEqual(gotMap, wantMap) {
t.Errorf("GetJSONFields() = %v, want %v", got, tc.want)
}
}
})
}
}

0 comments on commit b8a1c85

Please sign in to comment.