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 00000000..dc6e09ed --- /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 a2ecb3ed..a41471ed 100644 --- a/spdx/v2/v2_2/package.go +++ b/spdx/v2/v2_2/package.go @@ -75,7 +75,7 @@ type Package struct { // 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 @@ type Package struct { 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 @@ func (p *Package) UnmarshalJSON(b []byte) error { } 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.