question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Better support for polymorphism(replacement for discriminator)

See original GitHub issue

Many people including myself asked for full support of JSON Schema. But I totally understand why oneOf & anyOf are causing a lot of problems: It was never intended to use for anything beyond validation.

discriminator was good attempt to solve most of the common use cases. We tried it with our customers and even have added support for it in our documentation engine. But we have discovered a few issues that limit usability of discriminator:

  1. The value of discriminator field should match one of the keys in definitions object. If you use multiple discriminator inside multiple payloads you will experience name clashes. It also breaks namestyle, since a lot of people name schema in camelcase and field values using underscores. And with #634 merged(schema names limited to a-zA-Z0-9.-_\) it creates a big problem for non-English field values.
  2. There is no support for the default value of discriminator.
  3. Limited support in tooling; IMHO limiting factor is that it very hard to detect which types are inherited. You need to go through all schemas and inspect allOfs and it becomes especially problematic when inherited schemas exist in separate files.
  4. I’m not aware of any validator that supports discriminator. A solution would be to provide translator of discriminator into oneOf and use standard JSON Schema validator afterwards. But translation is hard to implement because of the third point.

My proposal is to replace discriminator with a new valueSwitch keyword. Here is modifiend discriminator example:

definitions:
  Pet:
    type: object
    properties:
      name:
        type: string
      petType:
        type: string
    required:
    - name
    - petType
    # Proposed keyword
    valueSwitch:
      # Name of the property which values are used to determinate apropriate subschema
      property: petType
      # Map of property values on Schema Objects
      values:
        Cat:
           $ref: '#/definitions/CatMixin'
        Dog:
           $ref: '#/definitions/DogMixin'
      # Schema which should be used when property is missing or
      # it's value doesn't match any key from `values` object
      default:
        description: Use specified pet type
        properties:
          ICZN:
            description: International Code of Zoological Nomenclature
            type: string
        required:
          - ICZN
  CatMixin:
    description: A representation of a cat
    type: object
    properties:
      huntingSkill:
        type: string
        description: The measured skill for hunting
        default: lazy
        enum:
        - clueless
        - lazy
        - adventurous
        - aggressive
    required:
    - huntingSkill
  DogMixin:
    description: A representation of a dog
    type: object
    properties:
      packSize:
        type: integer
        format: int32
        description: the size of the pack the dog is from
        default: 0
        minimum: 0
    required:
    - packSize

In the example I also added support for user-defined petType to illustrate usage of default keyword. And here is how it solves problems mentioned above:

  1. field values from payload are used only as keys of values object.
  2. Support for default, see above example.
  3. To fully understand schema you need to go only through schema itself and all schemas referenced from it.
  4. You can translate above valueSwitch into pure JSON Schema Draft4 using straightforward algorithm, for example:
oneOf:
  - type: object
    required:
      - petType
    properties:
      petType:
        enum:
          - Cat
    allOf:
      - $ref: '#/definitions/CatMixin'
  - type: object
    required:
      - petType
    properties:
      petType:
        enum:
          - Dog
    allOf:
      - $ref: '#/definitions/DogMixin'
  - properties:
      petType:
        not:
          enum:
            - Cat
            - Dog
    allOf:
      description: Use specified pet type
      properties:
        ICZN:
          description: International Code of Zoological Nomenclature
          type: string
      required:
        - ICZN

Moreover it is possible to convert discriminator into valueSwitch for all existing Swagger specs.

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:5
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

4reactions
IvanGoncharovcommented, Jun 7, 2016

Would I have to “flatten” the inheritance hierarchy by adding members HuntingDog and ShepherdDog to the valueSwitch/values object and provide an array of incremental mixins for them?

@ralfhandl You just need to use allOf for it:

    valueSwitch:
      # Name of the property which values are used to determinate apropriate subschema
      property: petType
      # Map of property values on Schema Objects
      values:
        Cat:
           $ref: '#/definitions/CatMixin'
        Dog:
           $ref: '#/definitions/DogMixin'
        HuntingDog:
           allOf:
             - $ref: '#/definitions/DogMixin'
             - $ref: '#/definitions/HuntingDogMixin'
        ShepherdDog:
           allOf:
             - $ref: '#/definitions/DogMixin'
             - $ref: '#/definitions/ShepherdDogMixin'

values has normal Schema Objects as values, so you can use everything that Swagger support: allOf, $ref, type, etc. You can even add nested valueSwitch 😄

This feels strange. Strong-typed languages will most likely have a problem with this sort of specification, given that on-the-fly framework conversions will fail. For example what should the request object type actually be? (Or what is the response object type?)

@wparad It possible to implement in all typed-languages I know of, most of them natively support polymorphism and the rest support “union”-like types. You can even implement it in pure C, using union type. What’s important you always have access to property used for switching, petType in above example. On implementation side it very simple you just add single switch which executed in runtime and it exactly the same as current discriminator.

It would be nice to be able to specify different validations–i.e. C if A or D if B, but I’m not sure that this is the best approach.

In really most of the time developers used only two patterns for dynamic data in JSON:

  1. Attribute where different values signalize different payloads, covered by this proposal
  2. Different types, for example, single string or array of strings. I working on a similar proposal, which adds new typeSwitch keyword.

To handle exotic cases you need to add something very similar to oneOf and that’s clearly not going to happen. So instead of creating some theoretical all in one solution let’s solve real-life problems without introducing too much complexity into tooling and the spec itself.

2reactions
fehguycommented, Jun 9, 2016

I’d like to suggest another approach to handling the general idea here. How about we get some examples of different modeling challenges and then put together a comprehensive solution from them? Maybe it’s been done but It would be great to start linking to them here.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Inheritance and Polymorphism - Swagger
To help API consumers detect the object type, you can add the discriminator/propertyName keyword to model definitions. This keyword points to the property...
Read more >
How to use the OpenAPI discriminator - Redocly
A better alternative is to use the mapping property and making the names explicitly declared. The possible values are determined from ...
Read more >
Using type discriminator results in strange database behaviour
Welcome to the MongoDB Community Forums. I understand that you're having some challenges mapping polymorphic types using the .NET/C# driver. The ...
Read more >
Replace Conditional with Polymorphism - Refactoring.Guru
Problem: You have a conditional that performs various actions depending on object type or properties. Solution: Create subclasses matching the branches of ...
Read more >
Genotyping of Single Nucleotide Polymorphisms by 5 - NCBI
In this chapter, we describe the 5′ nuclease allelic discrimination assay for ... a wild-type SNP Allele “A” is amplified separately from the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found