Skip to content

additionalProperties and OneOf at the same time #763

@B-Lorentz

Description

@B-Lorentz

The following spec:

openapi: 3.0.1

info:
  title: OpenAPI-CodeGen Test
  description: 'This is a test OpenAPI Spec'
  version: 1.0.0

servers:
  - url: https://test.oapi-codegen.com/v2
  - url: http://test.oapi-codegen.com/v2

paths:
  /cat:
    get:
      tags:
        - cat
      summary: Get cat status
      operationId: getCatStatus
      responses:
        200:
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Cat'
                

components:
  schemas:
    Cat:
      additionalProperties: true
      discriminator:
        mapping:
          alive: '#/components/schemas/CatAlive'
          dead: '#/components/schemas/CatDead'
        propertyName: state
      oneOf:
       - $ref: '#/components/schemas/CatAlive'
       - $ref: '#/components/schemas/CatDead'
    
    CatAlive:
      properties:
        state:
          type: string
          enum: [alive]
        name:
          type: string
        alive_since:
          type: string
          format: date-time
      required:
       - state

    CatDead:
      properties:
        state:
          type: string
          enum: [dead]
        name:
          type: string
        dead_since:
          type: string
          format: date-time
        cause:
          type: string
          enum: [car, dog, oldage, quantum-experiment]
      required:
       - state

Generates invalid code, because both the additionalProperties and the OneOf logic both generates a separate implementation of MarshallJSON and UnmarshallJSON, eg:

// Override default JSON handling for Cat to handle AdditionalProperties
func (a *Cat) UnmarshalJSON(b []byte) error {
	object := make(map[string]json.RawMessage)
	err := json.Unmarshal(b, &object)
	if err != nil {
		return err
	}

	if len(object) != 0 {
		a.AdditionalProperties = make(map[string]interface{})
		for fieldName, fieldBuf := range object {
			var fieldVal interface{}
			err := json.Unmarshal(fieldBuf, &fieldVal)
			if err != nil {
				return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err)
			}
			a.AdditionalProperties[fieldName] = fieldVal
		}
	}
	return nil
}

vs

func (t *Cat) UnmarshalJSON(b []byte) error {
	err := t.union.UnmarshalJSON(b)
	return err
}

Is this case supposed to be supported? I didn't find a clear statement of open-api spec saying that yes, this case is positively supported, but https://editor.swagger.io seems to understand it:
image
However, one can work around this by setting additionalProperties on the individual oneOf elements, so maybe it isn't that high-priority overall.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions