fix(2.2 json): remove emtpy packageVerificationCode (#223)

Signed-off-by: Keith Zantow <kzantow@gmail.com>
diff --git a/spdx/v2/v2_2/json/empty_values_test.go b/spdx/v2/v2_2/json/empty_values_test.go
new file mode 100644
index 0000000..dc6e09e
--- /dev/null
+++ b/spdx/v2/v2_2/json/empty_values_test.go
@@ -0,0 +1,60 @@
+package json
+
+import (
+	"encoding/json"
+	"github.com/spdx/tools-golang/spdx/v2/common"
+	spdx "github.com/spdx/tools-golang/spdx/v2/v2_2"
+	"github.com/stretchr/testify/require"
+	"testing"
+)
+
+func Test_omitsAppropriateProperties(t *testing.T) {
+	tests := []struct {
+		name     string
+		pkg      spdx.Package
+		validate func(t *testing.T, got map[string]interface{})
+	}{
+		{
+			name: "include packageVerificationCode exclusions",
+			pkg: spdx.Package{
+				PackageVerificationCode: common.PackageVerificationCode{
+					ExcludedFiles: []string{},
+				},
+			},
+			validate: func(t *testing.T, got map[string]interface{}) {
+				require.Contains(t, got, "packageVerificationCode")
+			},
+		},
+		{
+			name: "include packageVerificationCode value",
+			pkg: spdx.Package{
+				PackageVerificationCode: common.PackageVerificationCode{
+					Value: "1234",
+				},
+			},
+			validate: func(t *testing.T, got map[string]interface{}) {
+				require.Contains(t, got, "packageVerificationCode")
+			},
+		},
+		{
+			name: "omit empty packageVerificationCode",
+			pkg: spdx.Package{
+				PackageVerificationCode: common.PackageVerificationCode{},
+			},
+			validate: func(t *testing.T, got map[string]interface{}) {
+				require.NotContains(t, got, "packageVerificationCode")
+			},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			got, err := json.Marshal(test.pkg)
+			require.NoError(t, err)
+			var unmarshalled map[string]interface{}
+			err = json.Unmarshal(got, &unmarshalled)
+			require.NoError(t, err)
+			test.validate(t, unmarshalled)
+		})
+	}
+}
diff --git a/spdx/v2/v2_2/package.go b/spdx/v2/v2_2/package.go
index a2ecb3e..a41471e 100644
--- a/spdx/v2/v2_2/package.go
+++ b/spdx/v2/v2_2/package.go
@@ -75,7 +75,7 @@
 	// 7.14: All Licenses Info from Files: SPDX License Expression, "NONE" or "NOASSERTION"
 	// Cardinality: mandatory, one or many if filesAnalyzed is true / omitted;
 	//              zero (must be omitted) if filesAnalyzed is false
-	PackageLicenseInfoFromFiles []string `json:"licenseInfoFromFiles"`
+	PackageLicenseInfoFromFiles []string `json:"licenseInfoFromFiles,omitempty"`
 
 	// 7.15: Declared License: SPDX License Expression, "NONE" or "NOASSERTION"
 	// Cardinality: mandatory, one
@@ -123,6 +123,32 @@
 	hasFiles []common.DocElementID
 }
 
+func (p Package) MarshalJSON() ([]byte, error) {
+	type pkg Package
+	p2 := pkg(p)
+
+	data, err := marshal.JSON(p2)
+	if err != nil {
+		return nil, err
+	}
+
+	// remove empty packageVerificationCode entries -- required by SPDX 2.2 but
+	// omitempty has no effect since it is a non-comparable struct and not a pointer, so we
+	// manually check to determine if there is a valid value to output and omit the field if not
+	// see: https://spdx.github.io/spdx-spec/v2.2.2/package-information/#79-package-verification-code-field
+	if p.PackageVerificationCode.Value == "" && p.PackageVerificationCode.ExcludedFiles == nil {
+		var values map[string]interface{}
+		err = json.Unmarshal(data, &values)
+		if err != nil {
+			return nil, err
+		}
+		delete(values, "packageVerificationCode")
+		return marshal.JSON(values)
+	}
+
+	return data, nil
+}
+
 func (p *Package) UnmarshalJSON(b []byte) error {
 	type pkg Package
 	type extras struct {
@@ -154,6 +180,7 @@
 }
 
 var _ json.Unmarshaler = (*Package)(nil)
+var _ json.Marshaler = (*Package)(nil)
 
 // PackageExternalReference is an External Reference to additional info
 // about a Package, as defined in section 7.21 in version 2.2 of the spec.