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.

Deprecate discriminator?

See original GitHub issue

I just created an example to illustrate what I think is idiomatic use of discrimator, so I could help answer #2141. I find it helpful to use JSON Schema Lint so I can validate in real-time. To make sure the discriminating logic worked correctly in a standard JSON Schema validator (not aware of OAS discriminator), I used standard JSON Schema keywords to duplicate that logic.

This begs the question: now that 3.0 supports a large-enough subset of JSON Schema to describe discriminated subtypes, and 3.1 is planned to support full JSON Schema, do we still need discriminator?

@handrews mentions the same idea here in #2031, so I think the idea deserves its own issue for future version planning.

I can see that discriminator might have some value for code generators. It might be easier for a code generator to be told explicitly to discriminate based on a given property, rather than relying on a JSON Schema validator to identify the matched subtype, or recognizing a pattern involving oneOf, enum (or const), etc.

But discriminator, as it’s defined, is kind of problematic. @handrews pointed out that it ignores some available JSON Schema semantics. And I’ve observed, generally, that the documentation is difficult (for me) to follow, and seems to leave some questions unanswered. Without trying to create a comprehensive list here:

  • I have doubts about the combination of mapping and default name matching shown in the last example of this section.
  • It seems that mapping is supposed to be supplemental to name matching, rather than replacing it. In that case, is there a way for the discriminator to ensure that the discriminator property value is one of a known list of subtypes? Do we always need a separate oneOf to validate this?
  • Does name matching (without mapping) assume it’s going to find the named subtype schema in #/components/schemas, or is there some other expectation?

Maybe this is more weight than we need to carry, and we’d be better off leaving this problem to JSON Schema and some new vocabulary for code generation, O-O type system mapping, or something like that.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:10
  • Comments:102 (45 by maintainers)

github_iconTop GitHub Comments

10reactions
mkistlercommented, Mar 22, 2020

In the hope that it will inform this discussion, I’ll describe how we (IBM) are using discriminators in our APIs and code generation tools.

Here’s an example use of discriminator in the API for the IBM Discovery service:

      "QueryAggregation": {
        "type": "object",
        "description": "An aggregation produced by  Discovery to analyze the input provided.",
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "histogram": "#/components/schemas/Histogram",
            "max": "#/components/schemas/Calculation",
            "min": "#/components/schemas/Calculation",
            "average": "#/components/schemas/Calculation",
            "sum": "#/components/schemas/Calculation",
            "unique_count": "#/components/schemas/Calculation",
            "term": "#/components/schemas/Term",
            "filter": "#/components/schemas/Filter",
            "nested": "#/components/schemas/Nested",
            "timeslice": "#/components/schemas/Timeslice",
            "top_hits": "#/components/schemas/TopHits"
          }
        },
        "properties": {
          "type": {
            "type": "string",
            "description": "The type of aggregation command used. For example: term, filter, max, min, etc."
          },

QueryAggregation is “class” of schemas that may be returned from a query that requests aggregation of results in various forms. The various aggregation types vary quite significantly, but notice that some types, e.g. “max”, “min”, “average”, share a common schema. In this particular case, the “child” schemas are composed using allOfwith QueryAggregation as one element and then the specific properties of the child in a second element. E.g.


      "Calculation": {
        "allOf": [
          {
            "$ref": "#/components/schemas/QueryAggregation"
          },
          {
            "properties": {
              "field": {
                "type": "string",
                "description": "The field where the aggregation is located in the document."
              },
              "value": {
                "type": "number",
                "format": "double",
                "description": "Value of the aggregation."
              }
            }
          }
        ]
      },

Next I’ll describe how this is used in our tooling. The first thing to say about our SDK generation tooling is that it does not do any validation based on JSON schema. Some may consider this heresy, but we use the schemas in the API def purely for modeling.

In Java and similar type-strict languages, the QueryAggregation schema is rendered as a public class (it is not abstract, but if the composition were “flipped” to use oneOf it would be). The “child” schemas are rendered as subclasses of QueryAggregation, e.g. Calculation.

The discriminator in the QueryAggregation schema is rendered into the QueryAggregation class as static metadata:

  protected static String discriminatorPropertyName = "type";
  protected static java.util.Map<String, Class<?>> discriminatorMapping;
  static {
    discriminatorMapping = new java.util.HashMap<>();
    discriminatorMapping.put("histogram", Histogram.class);
    discriminatorMapping.put("max", Calculation.class);
    discriminatorMapping.put("min", Calculation.class);
    discriminatorMapping.put("average", Calculation.class);
    discriminatorMapping.put("sum", Calculation.class);
    discriminatorMapping.put("unique_count", Calculation.class);
    discriminatorMapping.put("term", Term.class);
    discriminatorMapping.put("filter", Filter.class);
    discriminatorMapping.put("nested", Nested.class);
    discriminatorMapping.put("timeslice", Timeslice.class);
    discriminatorMapping.put("top_hits", TopHits.class);
  }

This metadata is used by our deserialization logic to trigger and guide the use of a custom TypeAdapter that is created in DiscriminatorBasedTypeAdapterFactory. The custom TypeAdapter uses the value of the discriminator to choose a concrete class, based on the discriminatorMapping metadata in the class, to be produced by the deserialization logic.

If there were no discriminator, the generated code would look very different. We would instead create a QueryAggregation class containing the union of all the properties of the child schemas, and the returned class would be an instance of this “generic” QueryAggregation class.

Sorry for the long post. I hope this has been clear and informative.

9reactions
tedepsteincommented, Mar 2, 2020

is there any reason to keep discriminator, aside from legacy continuity (which admittedly is important), other than convenience (i.e. easier read, write or generate code from the schema)?

Clarity of intent. Standardized, unambiguous way to denote tagged unions, rather than relying on a loosely defined convention. “Convenience” translates into availability of code generators, validators and other tools: more of them, better consistency, and higher quality. All good things for the OpenAPI ecosystem and user community. These are the reasons described in recent posts here.

I’m not arguing that it’s ideal to keep discriminator in its current form, in OpenAPI, forever. But I would like to see it replaced by a JSON Schema vocabulary that has the same level (at least) of clarity, consistency, and simplicity.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to use the OpenAPI discriminator - Redocly
When an API can return two or more different types of objects (aka polymorphism), use oneOf or anyOf to describe those schemas (a...
Read more >
OpenAPI Specification - Version 3.0.3 - Swagger
The OpenAPI Specification defines a standard interface to RESTful APIs which allows both humans and computers to understand service capabilities without ...
Read more >
The Discriminator Column Of The Persistent Type Is Set But ...
Solution. To resolve this error, either remove the discriminator column or map the persistent class to the discriminator column's table. To remove the ......
Read more >
Configure the Logging Discriminator in Cisco IOS
A discriminator is a big, fancy word we use in Cisco IOS for a filter that acts on our logs. ... You wish...
Read more >
Embedded - Morphia
Modifier and Type, Optional Element, Description. String, discriminator. Deprecated. String, discriminatorKey. Deprecated. boolean, useDiscriminator.
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