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

Checking support for the key words “oneOf” and “allOf” #72

Open
Markus-Elfring opened this issue Nov 14, 2022 · 15 comments
Open
Labels
bug Something isn't working

Comments

@Markus-Elfring
Copy link

Questions

I took another look at the capabilities of the following software.

Version

4.3.4

Context

I constructed the following JSON schema file.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Calculation information",
  "description": "settings for calculations",
  "$id": "https://example.com/schemas/calculation_preferences",
  "type": "object",
  "required": ["context"],
  "properties":
  {
    "context":
    {
      "type": "object",
      "oneOf":
      [
        {
          "allOf":
          [
            {
              "properties":
              {
                "label": {"const": "GTIN"}
              }
            },
            {
              "type": "array",
              "minItems": 1,
              "items":
              {
                "type": "string",
                "pattern": "^[0-9]{13}$"
              }
            }
          ]
        },
        {
          "allOf":
          [
            {
              "properties":
              {
                "label": {"const": "selection group"}
              }
            },
            {
              "type": "array",
              "minItems": 1,
              "items":
              {
                "type": "string",
                "minLength": 1
              }
            }
          ]
        }
      ]
    }
  }
}

Test data example:

{
  "context":
  {
    "GTIN": [ "1234567890123" ]
  }
}

💭 Now I am wondering about the following error information.

Markus_Elfring@…:…/Schema-Parsing3> java -jar target/Schema-Parsing3-0.0.1-SNAPSHOT.jar ../Context-test_fragment1.json
Validation errors
error|keyword|keywordLocation|instanceLocation
Property "context" does not match schema|properties|#/properties|#
Instance does not match exactly one subschema (0 matches)|oneOf|#/properties/context/oneOf|#/context
Instance does not match every subschema|allOf|#/properties/context/oneOf/0/allOf|#/context
Instance type object is invalid. Expected array|type|#/properties/context/oneOf/0/allOf/1/type|#/context
Instance does not match every subschema|allOf|#/properties/context/oneOf/1/allOf|#/context
Instance type object is invalid. Expected array|type|#/properties/context/oneOf/1/allOf/1/type|#/context
java.lang.RuntimeException: JSON schema validation failure
        at main.java.….app.JsonSchemaCheck.reportErrors(CLI.java:47)
        at main.java.….app.JsonSchemaCheck.test4(CLI.java:116)
        at main.java.….app.JsonSchemaCheck.call(CLI.java:129)
        at main.java.….app.JsonSchemaCheck.call(CLI.java:20)
        at picocli.CommandLine.executeUserObject(CommandLine.java:1953)
        at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2358)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2352)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2314)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2179)
        at picocli.CommandLine$RunLast.execute(CommandLine.java:2316)
        at picocli.CommandLine.execute(CommandLine.java:2078)
        at main.java.….app.JsonSchemaCheck.main(CLI.java:134)

🤔 I would appreciate further advices and solution ideas for these reported data type mismatches.

@Markus-Elfring Markus-Elfring added the bug Something isn't working label Nov 14, 2022
@jdesrosiers
Copy link

jdesrosiers commented Nov 15, 2022

Let's look at this part of your schema.

"allOf": [
  {
    "properties": {
      "label": { "const": "GTIN" }
    }
  },
  {
    "type": "array",
    "items": {
      "type": "string",
      "pattern": "^[0-9]{13}$"
    },
    "minItems": 1
  }
]

The allOf keyword means that all of the constraints in both schemas are applied. Since the second schema declares "type": "array" and the properties keyword only applies to objects, the whole first schema doesn't do anything useful. I'm not sure what you were trying to do with this schema, so it's difficult for me to advise you on how to fix it.

If I remove the parts that aren't doing anything, we end up with the following, which shows another problem.

  "oneOf": [
    {
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^[0-9]{13}$"
      },
      "minItems": 1
    },
    {
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      },
      "minItems": 1
    }
  ]

The oneOf keyword is an exclusive OR operation. That means that one and only one of the schemas must be valid. Anything that passes the first schema will also pass the second schema. So, the only thing that will pass this schema is an array with string objects that don't match the given pattern. I'm sure that's not what you meant, I don't know enough about what the desired outcome is to advise on how to fix it.

Next, I've refactored the schema again. The behavior of the schema still hasn't changed, but I did change a couple keywords in order to simplify it.

{
  "type": "object",
  "allOf": [
    {
      "type": "array",
      "items": {
        "type": "string",
        "not": { "pattern": "^[0-9]{13}$" }
      },
      "minItems": 1
    }
  ]
}

Now we see the biggest problem with the schema. This is saying the value must be an object AND that it must be an array. Of course that's not possible, so this sub-schema will always fail no matter what value it's validating.

So, if we refactor the schema one last time, we end up with the following.

{
  "type": "object",
  "properties": {
    "context": false
  },
  "required": ["context"]
}

The "context" property is required by the required keyword, but the properties keyword will fail if the "context" property is present. No values can successfully validate against this schema. You're entire schema simplifies to false.

Here's my best guess at the structure you were trying to describe.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/schemas/calculation_preferences",

  "type": "object",
  "properties": {
    "context": {
      "type": "object",
      "properties": {
        "GTIN": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[0-9]{13}$"
          },
          "minItems": 1
        },
        "selection group": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1
        }
      },
      "oneOf": [
        { "required": ["GTIN"] },
        { "required": ["selection group"] }
      ]
    }
  },
  "required": ["context"]
}

@Markus-Elfring
Copy link
Author

… and the properties keyword only applies to objects,

This is fine.

the whole first schema doesn't do anything useful.

I suggest to reconsider this view a bit more.

I'm not sure what you were trying to do with this schema,

Do you find the shown test data example helpful and reasonable?

I am trying to switch an object property according to its identifier or “label”.

so it's difficult for me to advise you on how to fix it.

Can the understanding evolve for more desirable data model design approaches?

If I remove the parts that aren't doing anything, we end up with the following, …

It seems that you interpreted the JSON schema code in a direction which I did not intent.

Here's my best guess at the structure you were trying to describe.

I interpret your suggestion in the way that an object would get the properties “GTIN” and “selection group”.
I would like to achieve that only one of them should be used.
I got the impression that a suggestion by Henry Andrews from 2022-07-21 could be applied accordingly (until the schema key word “propertyDependencies” will become officially supported).

@Markus-Elfring
Copy link
Author

The message “Instance type object is invalid. Expected array” is generated by the implementation of the method “validate” from the class “SchemaValidatorImpl”.
💭 Will any additional case distinctions become relevant here?

@jdesrosiers
Copy link

It seems that you interpreted the JSON schema code in a direction which I did not intent.

With the exception of the last schema, I'm not trying to interpret your intent. I'm trying to help you understand what your schema actually describes, not what you intended it to describe. Everything I told you about how the schema behaves as written is correct.

Do you find the shown test data example helpful and reasonable?

It was helpful, but it wasn't enough for me to understand what you're trying to do, especially because it looks quite different than what your schema describes. Your schema seems to describe a complex assertion, but you only have one example and don't say whether it should pass or fail. If you can provide more examples including whether they should pass or fail validation, I can be more confident about what you want the schema to describe.

I interpret your suggestion in the way that an object would get the properties “GTIN” and “selection group”.
I would like to achieve that only one of them should be used.

      "oneOf": [
        { "required": ["GTIN"] },
        { "required": ["selection group"] }
      ]

That is the part of my suggested schema that asserts that one and only one of the properties "GTIN" and "selection group" can be used in the "context" object.

I got the impression that a suggestion by Henry Andrews from 2022-07-21 could be applied accordingly (until the schema key word “propertyDependencies” will become officially supported).

Based on my best guess about the structure you are trying to describe, I don't think that pattern (or propertyDependencies) applies to what you are trying to do. That pattern doesn't do what you think it does. I think Henry misunderstood what you were trying to do because you didn't provide enough information about what you needed.

@Markus-Elfring
Copy link
Author

…, but you only have one example and don't say whether it should pass or fail.

The shown test data should get accepted (“pass”) by a corresponding JSON schema file.

💭 Can you agree to the expectation that some information should be applied to an item only after the affected item got a label assigned (for the discussed use case)?

That is the part of my suggested schema that asserts that one and only one of the properties "GTIN" and "selection group" can be used in the "context" object.

I interpret this specification variant in the way that one property would become required while other properties can still be optional.
Optional items are not wanted in the data model at this place.

…, I don't think that pattern (or propertyDependencies) applies to what you are trying to do.

💭 Do you find any other specification approach more appropriate for guaranteeing that customised data structure settings are mapped to one configurable identifier?

I think Henry misunderstood what you were trying to do because you didn't provide enough information about what you needed.

🤔 Thus your view made me curious if involved developers (like @handrews) will get into the mood again to adjust remaining communication difficulties any further.

@jdesrosiers
Copy link

I interpret this specification variant in the way that one property would become required while other properties can still be optional.

That's not correct. The oneOf keyword asserts that one and only one of the schemas can pass validation. If both pass, the assertion fails. if both fail, the assertion fails. The assertion will only pass if one passes and the other fails. In other words, the object is valid if one of "GTIN" or "selection group" is present, but not both.

If you haven't already, try the schema I gave in a validator with some example data. If you find example data that doesn't validate the way you expect, then share that example here and we can work from there.

@Markus-Elfring
Copy link
Author

Markus-Elfring commented Nov 18, 2022

The oneOf keyword asserts that one and only one of the schemas can pass validation.

This functionality can generally be helpful.

In other words, the object is valid if one of "GTIN" or "selection group" is present, but not both.

This is the outcome which would be desirable for my data model.

But: 🤔

  • Does your specification variant express that both properties are available for the object “context”?
  • How would you categorise and handle a property which would be “not required”?

If you find example data that doesn't validate the way you expect, …

💭 I would prefer to achieve consensus on the aspect if the suggestion by Henry Andrews from 2022-07-21 (according to an intermediate design alternative for the functionality “propertyDependencies”) can produce the same validation result finally.

@handrews
Copy link

@Markus-Elfring As best I can tell, @jdesrosiers is correct to state that I did not (and still do not) understand your question or use case. Therefore my suggestion probably is not what you were looking for and should not be considered authoritative. In any event, there's nothing that needs to be done or decided or resolved about that suggestion. It works as it is, and you can use it if you want.

@jdesrosiers
Copy link

Does your specification variant express that both properties are available for the object “context”?

Both properties are allowed, but only one at a time. Seriously, try it out and see what happens. You can learn a lot about how it works with a little trial and error.

How would you categorise and handle a property which would be “not required”?

All properties in JSON Schema are optional unless explicitly required using the required keyword. So, just add a property to properties and you have a property that is not required.

I would prefer to achieve consensus on the aspect if the suggestion by Henry Andrews from 2022-07-21 (according to an intermediate design alternative for the functionality “propertyDependencies”) can produce the same validation result finally.

Here are some examples of what Henry thought you were trying to do describe. The "label" property decides which of "GTIN" or "selection group" is allowed/required.

{
  "label": "GTIN",
  "GTIN": [ "1234567890123" ]
}
{
  "label": "selection group",
  "selection group": [ "foo" ]
}

All of the solutions presented in that thread (there were three) were for describing a structure like what is shown in these examples. These examples are not how your data is structured and therefore, none of those solutions apply to this case.

@Markus-Elfring
Copy link
Author

… is correct to state that I did not (and still do not) understand your question or use case.

@handrews: I find such a feedback strange.

💭 I guess that you are very familiar with assigning different labels also to arrays (or objects) on demand.

In any event, there's nothing that needs to be done or decided or resolved about that suggestion.

💭 I got an other impression from the presented understanding difficulties.

It works as it is,

💭 I hope so (because it looked so promising).

and you can use it if you want.

💭 I would like to extend its usage accordingly.

Unfortunately, it seems that some involved developers can get confused.

@Markus-Elfring
Copy link
Author

…, but only one at a time.

Such an effect can be desirable.

💭 It seems that an opposite understanding of data structure settings needs to be resolved.

You can learn a lot about how it works with a little trial and error.

💭 I suggest to reconsider this view once more if it should be determined how a data validation software should behave according to the official JSON schema specification.

All properties in JSON Schema are optional unless explicitly required using the required keyword.

I find this feedback interesting.

💭 How does it fit to a different understanding for the handling of optional (“not required”) items?

So, just add a property to properties and you have a property that is not required.

I would like to work only with required items in the discussed data model which excludes optional parts.

The "label" property decides which of "GTIN" or "selection group" is allowed/required.

💭 It can be that the property “required” should be additionally specified for the desired identifier.
I hope that an extra property “label” can be avoided then.

@jdesrosiers
Copy link

It seems we've yet again reached the point where I can't understand you well enough to be of help to you. I'm trying, but it's been really hard to help you. I've shown you a schema that describes the structure you're trying to describe. You've refused to try the schema and see that it works and instead, you continue to guess incorrectly about what the schema does. I've shown in detail why the method you're original method doesn't work, yet you remain fixated on that approach.

@Markus-Elfring
Copy link
Author

I've shown you a schema that describes the structure you're trying to describe.

Your specification approach provides hints for further development considerations.

💭 Do you find the following data format description variant easier to understand?

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Calculation information",
  "description": "settings for calculations",
  "$id": "https://example.com/schemas/calculation_preferences",
  "type": "object",
  "required": ["context"],
  "properties":
  {
    "context":
    {
      "type": "object",
      "oneOf":
      [
        {
          "required": ["GTIN"],
          "GTIN":
          {
            "type": "array",
            "minItems": 1,
            "items":
            {
              "type": "string",
              "pattern": "^[0-9]{13}$"
            }
          }
        },
        {
          "required": ["selection group"],
          "selection group":
          {
            "type": "array",
            "minItems": 1,
            "items":
            {
              "type": "string",
              "minLength": 1
            }
          }
        }
      ]
    }
  }
}

@jdesrosiers
Copy link

You're close to something that will work. Your missing properties at /properties/context/oneOf/0 and /properties/context/oneOf/1. Any you'll also need to add "additionalProperties": false to each of those schemas.

{
  "properties": {
    "GTIN": { ... }
  },
  "required": ["GTIN"],
  "additionalProperties": false
}

This will work the same as the other schema I gave you, but I don't recommend doing it this way. It's less efficient, more complicated, and will produce very confusing and unhelpful error messages. The original way I showed you is a much better solution.

@Markus-Elfring
Copy link
Author

You're close to something that will work.

💭 Is such a view interesting also according to the aspect that a validation tool (which is based on the software “Vert.x JSON Schema 4.3.4”) accepted the mentioned data format description variant so far?

Any you'll also need to add "additionalProperties": false to each of those schemas.

Would you like to explain this suggestion a bit more?

This will work the same as the other schema I gave you, …

I got an other impression.

It's less efficient, more complicated, and will produce very confusing and unhelpful error messages.

💭 I hope also that the involved software components can be improved considerably.

The original way I showed you is a much better solution.

💭 It seems that we have not achieved consensus for the mentioned technical details so far.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants