Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generated Unmarshal for fields with time.Time #159

Open
rbellido-ut opened this issue Oct 20, 2023 · 5 comments
Open

Generated Unmarshal for fields with time.Time #159

rbellido-ut opened this issue Oct 20, 2023 · 5 comments

Comments

@rbellido-ut
Copy link

rbellido-ut commented Oct 20, 2023

This JSON schema generates the following types and UnmarshalJSON method:

// CloudEvents Specification JSON Schema
type CloudeventsSchemaJson struct {
	// The event payload.
	Data Datadef `json:"data,omitempty" yaml:"data,omitempty" mapstructure:"data,omitempty"`

	// Base64 encoded event payload. Must adhere to RFC4648.
	DataBase64 DataBase64Def `json:"data_base64,omitempty" yaml:"data_base64,omitempty" mapstructure:"data_base64,omitempty"`

	// Content type of the data value. Must adhere to RFC 2046 format.
	Datacontenttype Datacontenttypedef `json:"datacontenttype,omitempty" yaml:"datacontenttype,omitempty" mapstructure:"datacontenttype,omitempty"`

	// Identifies the schema that data adheres to.
	Dataschema Dataschemadef `json:"dataschema,omitempty" yaml:"dataschema,omitempty" mapstructure:"dataschema,omitempty"`

	// Identifies the event.
	Id Iddef `json:"id" yaml:"id" mapstructure:"id"`

	// Identifies the context in which an event happened.
	Source Sourcedef `json:"source" yaml:"source" mapstructure:"source"`

	// The version of the CloudEvents specification which the event uses.
	Specversion Specversiondef `json:"specversion" yaml:"specversion" mapstructure:"specversion"`

	// Describes the subject of the event in the context of the event producer
	// (identified by source).
	Subject Subjectdef `json:"subject,omitempty" yaml:"subject,omitempty" mapstructure:"subject,omitempty"`

	// Timestamp of when the occurrence happened. Must adhere to RFC 3339.
	Time Timedef `json:"time,omitempty" yaml:"time,omitempty" mapstructure:"time,omitempty"`

	// Describes the type of event related to the originating occurrence.
	Type Typedef `json:"type" yaml:"type" mapstructure:"type"`
}

type DataBase64Def *string

type Datacontenttypedef *string

type Datadef interface{}

type Dataschemadef *string

type Iddef string

type Sourcedef string

type Specversiondef string

type Subjectdef *string

type Timedef *time.Time

type Typedef string

// UnmarshalJSON implements json.Unmarshaler.
func (j *CloudeventsSchemaJson) UnmarshalJSON(b []byte) error {
	var raw map[string]interface{}
	if err := json.Unmarshal(b, &raw); err != nil {
		return err
	}
	if v, ok := raw["id"]; !ok || v == nil {
		return fmt.Errorf("field id in CloudeventsSchemaJson: required")
	}
	if v, ok := raw["source"]; !ok || v == nil {
		return fmt.Errorf("field source in CloudeventsSchemaJson: required")
	}
	if v, ok := raw["specversion"]; !ok || v == nil {
		return fmt.Errorf("field specversion in CloudeventsSchemaJson: required")
	}
	if v, ok := raw["type"]; !ok || v == nil {
		return fmt.Errorf("field type in CloudeventsSchemaJson: required")
	}
	type Plain CloudeventsSchemaJson
	var plain Plain
	if err := json.Unmarshal(b, &plain); err != nil {
		return err
	}
	*j = CloudeventsSchemaJson(plain)
	return nil
}

The json.Unmarshal(data, &cloudevent) for it results in an error:

	err := json.Unmarshal([]byte(`{
		"data":{"exampleField":"a potato flew around", "enumField": "one potato"},
		"source":"mysource",
		"type":"public.domain.resource.action",
		"id":"7a929ab4-cd97-4208-8e49-98d9f4d88881",
		"time":"2023-08-02T17:53:08.614Z",
		"specversion":"1.0"
	}`), &cloudevent)
Failed to unmarshal JSON: &json.UnmarshalTypeError{Value:"string", Type:(*reflect.rtype)(0x11b6de0), Offset:88, Struct:"Plain", Field:"time"}

Is there a way for the generated UnmarshalJSON to account for Unmarshalling the Time field?

We've resorted to implementing our own UnmarshalJSON and passing --only-models in the generate command for the time being.

@omissis
Copy link
Owner

omissis commented Nov 7, 2023

Hi @rbellido-ut thanks for reporting this issue, I will look into it!

@omissis
Copy link
Owner

omissis commented Nov 7, 2023

@rbellido-ut I can't seem to reproduce your error, but I get a different one as time does not get imported correctly. what version of go-jsonschema are you using? Also, can you share the whole generated go file, including imports, please?

@omissis
Copy link
Owner

omissis commented Nov 7, 2023

I investigated the matter a bit today and it seems the problem is a bit more complex than I expected, as we are dealing with a nullable reference type here, which makes implementing custom unmarshalling the value correctly a bit of a challenge. I will try to see if I can find a solution in the next days, let's see.

@andrewpollock
Copy link

Hi,

I think I may have a related problem.

I'm operating on https://csrc.nist.gov/schema/nvd/api/2.0/cve_api_json_2.0.schema, and having invoked it as go-jsonschema -p cves /tmp/cve_api_json_2.0.schema have ended up with this particular struct:

type CveApiJson20Schema struct {
        // Format corresponds to the JSON schema field "format".
        Format string `json:"format" yaml:"format" mapstructure:"format"`

        // ResultsPerPage corresponds to the JSON schema field "resultsPerPage".
        ResultsPerPage int `json:"resultsPerPage" yaml:"resultsPerPage" mapstructure:"resultsPerPage"`

        // StartIndex corresponds to the JSON schema field "startIndex".
        StartIndex int `json:"startIndex" yaml:"startIndex" mapstructure:"startIndex"`

        // Timestamp corresponds to the JSON schema field "timestamp".
        Timestamp time.Time `json:"timestamp" yaml:"timestamp" mapstructure:"timestamp"`

        // TotalResults corresponds to the JSON schema field "totalResults".
        TotalResults int `json:"totalResults" yaml:"totalResults" mapstructure:"totalResults"`

        // Version corresponds to the JSON schema field "version".
        Version string `json:"version" yaml:"version" mapstructure:"version"`

        // NVD feed array of CVE
        Vulnerabilities []CveItem `json:"vulnerabilities" yaml:"vulnerabilities" mapstructure:"vulnerabilities"`
}

A sample of what I'm trying to decode is (see https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2023-4863):

{
"resultsPerPage": 1,
"startIndex": 0,
"totalResults": 1,
"format": "NVD_CVE",
"version": "2.0",
"timestamp": "2023-11-21T09:56:39.367",
"vulnerabilities": []
}

Whilst debugging why decoding was failing, I noticed this error:

time.ParseError {Layout: "2006-01-02T15:04:05Z07:00", Value: "2023-11-20T01:00:26.840", LayoutElem: "Z07:00", ValueElem: "", Message: ""}

I've been having some fun decoding the ISO 8601 format the NVD is using (see also https://mastodon.au/@andrewpollock/111447117663929594)

I'm not actually understanding how the time format "2006-01-02T15:04:05Z07:00" is getting arrived at. I know this is the time.RFC3339 constant, but I'm not seeing how things are defaulting to this? I think if I can override this (somewhere) to be "2006-01-02T15:04:05.999" instead, I might be in business...

@dstubbersfield
Copy link

@omissis @rbellido-ut wondering if you found a workaround to this? I believe I'm observing the same bug, before marshaling, my time.Time object is DateTime:{wall:134877000 ext:63851803830 loc:0x196c7e0} and afterwards DateTime:{wall:0 ext:0 loc:<nil>}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants