Using generated union type as response returning empty json
I have request that returning in response type Event defined like:
event:
oneOf:
- $ref: '#/components/schemas/eventTypeOnetimeEditableField'
- $ref: '#/components/schemas/eventTypeRepeatableEditableField'
Generated types are:
type Event struct {
union json.RawMessage
}
func (t Event) MarshalJSON() ([]byte, error) {
b, err := t.union.MarshalJSON()
return b, err
}
type PostDescribeEventById200JSONResponse Event
The problem is PostDescribeEventById200JSONResponse on json marshalling returning empty json "{}". This happens because PostDescribeEventById200JSONResponse is not using MarshalJSON() of underlying struct Event.
Using type alias instead of underlying type solving my problem:
type PostDescribeEventById200JSONResponse = Event
I can confirm that this is an issue indeed.
I fixed it by adding MarshalJSON method to (using your example) PostDescribeEventById200JSONResponse:
func (c PostDescribeEventById200JSONResponse) MarshalJSON() ([]byte, error) {
return Event(c).MarshalJSON()
}
did you solve this with a template @idleway ? @miro-ko did you add this manually to the generated file or do you have a nice way ? or is there any lead how to fix this issue long term ?
I would create a PR if I had a starting point for a solution
I have similar issue - in my case the field is just one of the fields that are contained in a containing response object. The generated code for the strict server interface does not marshall that imbedded union as part of the code that gets generated for the 201 response creation.
I spend some time digging around and this is what I found:
in the strict-interface.tmpl
https://github.com/deepmap/oapi-codegen/blob/ad62d73d8d22aa251d06b490b259790919899eab/pkg/codegen/templates/strict/strict-interface.tmpl#L44
we can see that the = for aliasing is bound to IsRef which is a function on the Schema
https://github.com/deepmap/oapi-codegen/blob/ad62d73d8d22aa251d06b490b259790919899eab/pkg/codegen/schema.go#L42-L44
digging deeper and following my response object I also found this in GenerateGoSchema:
https://github.com/deepmap/oapi-codegen/blob/ad62d73d8d22aa251d06b490b259790919899eab/pkg/codegen/schema.go#L233-L246
here is the place where the RefType could have been set but it is not but instead the GoType is populated with a var called refType I thought this could be a small typo and just changed it to
return Schema{
GoType: refType,
RefType: refType,
Description: schema.Description,
DefineViaAlias: true,
OAPISchema: schema,
}, nil
now this generates me aliased types and therefor the unions MarshalJSON function is used on the Visit func and the JSON body is correctly there
I am just not sure if this could break any cases I did not consider
since I need this I will fix this on my fork but I can also make a PR to fix this here
Is there any progress on this issue? I have the similar problem
I think this is the same issue as discussed here: https://github.com/deepmap/oapi-codegen/issues/1122#issuecomment-1726614770
It seems to be a problem when you have a content of response of type schema oneOf:
Foo:
schema:
oneOf:
- ref$: 'obj1'
- ref$: 'obj2'
Generates responses as:
type Foo200JSONResponse FooResponse
type FooResponse {
union json.RawMessage
}
which decodes as {} when:
func (response Foo200JSONResponse) VisitAcceptTicketResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response)
}
is called.
It would work if we would encode response.union and not response itself, as the encoding the response returns {} and not the content of the union.
Proposal that if the response has oneOf schema, then encode the response.union and not response as the 200 object is a type and not a struct.
func (response Foo200JSONResponse) VisitAcceptTicketResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response.union)
}
My PR #1231 has been working for me for a while which uses the fix mentioned above, however I have just found one small issue. I'm not even sure it's a real issue, but more a bit "meh".
If you have a schema in your specification which is just an alias for another type, i.e.
components:
schemas:
Path:
type: string
You end up with a type declaration:
type Path = string
The reason I'm wrapping this up in a defined type is I want extra methods on the Path type to provide certain logic, but now it's declared as a go alias this isn't possible.
It's not the end of the world, but if we could figure out how to differentiate these types of aliases versus union types that would be good.
Any progress?
It seems that there aren't any problems in the code generation of union type model.
The generation of strict response was handled independently in strict-interface.tmpl. I found that the .Schema.UnionElements of the strict response is empty during the generation (because it's a Defined Type of a union type, not the union type itself ?)
{{ ..Schema.TypeDecl }} may be the extractly same definition as the union type, but without calling the union.tmpl, the MarshalJSON wasn't generated.
I think we can fix this problem if we can
- identify if a response could be a union type or not in
strict-interface.tmpl(the only way I found is checking the OpenAPI schema through.Schema.OAPISchema) - or treat the strict response object as a trivial model (like the other union types, and generate the MarshalJSON for it)
I'm currently using the method 1. Patching the oapi-codegen through user-template works for me.
https://github.com/oapi-codegen/oapi-codegen/issues/1665#issuecomment-2179764402
We have exactly the same problem. Support for oneOf would be highly appreciated 🙏 as we now have well documented APIs without (fully working) automatic server/client generation.