Description
types.UnitBytes has custom MarshalJSON and MarshalYAML methods that serialize the value as a string, but no UnmarshalJSON or UnmarshalYAML methods. Since UnitBytes is type UnitBytes int64, Go's default unmarshalers expect a number and reject the string output from the custom marshalers.
This means any JSON or YAML round-trip of a types.UnitBytes value fails.
Minimal reproduction
package main
import (
"encoding/json"
"fmt"
"github.com/compose-spec/compose-go/v2/types"
"gopkg.in/yaml.v3"
)
func main() {
original := types.UnitBytes(655360)
fmt.Printf("Original value: %d (type: %T)\n\n", original, original)
// JSON round-trip fails
jsonBytes, _ := json.Marshal(original)
fmt.Printf("json.Marshal output: %s\n", jsonBytes)
var jsonResult types.UnitBytes
err := json.Unmarshal(jsonBytes, &jsonResult)
fmt.Printf("json.Unmarshal error: %v\n\n", err)
// YAML round-trip through untyped map
yamlBytes, _ := yaml.Marshal(original)
fmt.Printf("yaml.Marshal output: %s", yamlBytes)
var untypedValue interface{}
yaml.Unmarshal(yamlBytes, &untypedValue)
fmt.Printf("yaml.Unmarshal into interface{}: %v (type: %T)\n\n", untypedValue, untypedValue)
// Untyped value back through JSON also fails
jsonBytes2, _ := json.Marshal(untypedValue)
fmt.Printf("json.Marshal of untyped value: %s\n", jsonBytes2)
var jsonResult2 types.UnitBytes
err = json.Unmarshal(jsonBytes2, &jsonResult2)
fmt.Printf("json.Unmarshal back to UnitBytes error: %v\n", err)
}
Output:
Original value: 655360 (type: types.UnitBytes)
json.Marshal output: "655360"
json.Unmarshal error: json: cannot unmarshal string into Go value of type types.UnitBytes
yaml.Marshal output: "655360"
yaml.Unmarshal into interface{}: 655360 (type: string)
json.Marshal of untyped value: "655360"
json.Unmarshal back to UnitBytes error: json: cannot unmarshal string into Go value of type types.UnitBytes
Root cause:
In types/bytes.go, MarshalJSON returns a quoted string and MarshalYAML returns a string:
func (u UnitBytes) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"%d"`, u)), nil // "655360" — string in JSON
}
func (u UnitBytes) MarshalYAML() (interface{}, error) {
return fmt.Sprintf("%d", u), nil // "655360" — string
}
But there are no UnmarshalJSON or UnmarshalYAML methods, so Go falls back to the default int64 unmarshaler which rejects strings.
The existing DecodeMapstructure method handles both int and string, but that only works with the mapstructure library — not with encoding/json or gopkg.in/yaml.v3.
Context: This issue was discovered via score-spec/score-compose#470, where JSON/YAML round-tripping of types.Project fails when the project contains UnitBytes values.
Suggested fix:
Add UnmarshalJSON and UnmarshalYAML methods that accept both numeric and string values, consistent with the existing DecodeMapstructure logic. Happy to submit a PR if this approach works.
Description
types.UnitByteshas customMarshalJSONandMarshalYAMLmethods that serialize the value as a string, but noUnmarshalJSONorUnmarshalYAMLmethods. SinceUnitBytesistype UnitBytes int64, Go's default unmarshalers expect a number and reject the string output from the custom marshalers.This means any JSON or YAML round-trip of a
types.UnitBytesvalue fails.Minimal reproduction
Output:
Root cause:
In
types/bytes.go,MarshalJSONreturns a quoted string andMarshalYAMLreturns a string:But there are no
UnmarshalJSONorUnmarshalYAMLmethods, so Go falls back to the defaultint64unmarshaler which rejects strings.The existing
DecodeMapstructuremethod handles bothintandstring, but that only works with the mapstructure library — not withencoding/jsonorgopkg.in/yaml.v3.Context: This issue was discovered via score-spec/score-compose#470, where JSON/YAML round-tripping of
types.Projectfails when the project containsUnitBytesvalues.Suggested fix:
Add
UnmarshalJSONandUnmarshalYAMLmethods that accept both numeric and string values, consistent with the existingDecodeMapstructurelogic. Happy to submit a PR if this approach works.