57

I am trying to figure out how oneOf works by building a schema which validates two different object types. For example a person (firstname, lastname, sport) and vehicles (type, cost).

Here are some sample objects:

{"firstName":"John", "lastName":"Doe", "sport": "football"}

{"vehicle":"car", "price":20000}

The question is what have I done wrongly and how can I fix it. Here is the schema:

{
    "description": "schema validating people and vehicles", 
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "required": [ "oneOf" ],
    "properties": { "oneOf": [
        {
            "firstName": {"type": "string"}, 
            "lastName": {"type": "string"}, 
            "sport": {"type": "string"}
        }, 
        {
            "vehicle": {"type": "string"}, 
            "price":{"type": "integer"} 
        }
     ]
   }
}

When I try to validate it in this parser:

https://json-schema-validator.herokuapp.com/

I get the following error:

   [ {
  "level" : "fatal",
  "message" : "invalid JSON Schema, cannot continue\nSyntax errors:\n[ {\n  \"level\" : \"error\",\n  \"schema\" : {\n    \"loadingURI\" : \"#\",\n    \"pointer\" : \"/properties/oneOf\"\n  },\n  \"domain\" : \"syntax\",\n  \"message\" : \"JSON value is of type array, not a JSON Schema (expected an object)\",\n  \"found\" : \"array\"\n} ]",
  "info" : "other messages follow (if any)"
}, {
  "level" : "error",
  "schema" : {
    "loadingURI" : "#",
    "pointer" : "/properties/oneOf"
  },
  "domain" : "syntax",
  "message" : "JSON value is of type array, not a JSON Schema (expected an object)",
  "found" : "array"
} ]
Jonathan Hartley
  • 15,462
  • 9
  • 79
  • 80
Stanimirovv
  • 3,064
  • 8
  • 32
  • 56

2 Answers2

79

Try this:

{
    "description" : "schema validating people and vehicles",
    "type" : "object",
    "oneOf" : [
       {
        "type" : "object",
        "properties" : {
            "firstName" : {
                "type" : "string"
            },
            "lastName" : {
                "type" : "string"
            },
            "sport" : {
                "type" : "string"
            }
          }
      }, 
      {
        "type" : "object",
        "properties" : {
            "vehicle" : {
                "type" : "string"
            },
            "price" : {
                "type" : "integer"
            }
        },
        "additionalProperties":false
     }
]
}
Chaitanya Babar
  • 269
  • 3
  • 12
jruizaranguren
  • 12,679
  • 7
  • 55
  • 73
  • Does `oneOf` help in validating the request? I can't see any jackson annotation for `oneOf` during pojo conversion – gursahib.singh.sahni May 03 '17 at 08:50
  • 1
    @gursahib.singh.sahni, that is a jackson specific problem. It is not easy to translate oneOf semantics to some programming languages such as java. You need some construct like Disjoint sets, Discriminated Unions, Union Types... that other languages have. – jruizaranguren May 03 '17 at 09:47
  • @jruizaranguren there any library other than jackson that handles this case ? or else we would have to add custom validators in our codebase. – gursahib.singh.sahni May 03 '17 at 09:50
  • 1
    Is it required to have `"type" : "object"` on the root? It doesn't make much sense. – Alireza Mirian Dec 28 '17 at 12:29
  • @AlirezaMirian, if you do not explicitly specify it, any other type would satisfy the specification, because "oneOf" only applies to types. It is ignored for other types. – jruizaranguren Dec 29 '17 at 14:52
  • @jruizaranguren >""oneOf" only applies to types. It is ignored for other types" - It's wrong. But in this particular case, "type: object" is needed, because the specs inside `oneOf` do not have any type information and the type should be specified *somewhere*. – Ark-kun Jan 09 '19 at 20:14
  • @Ark-kun, it is convenient, not required. If type is omitted, unwanted objects may be validated. For instance, an empty object validates my schema, which is not probably what you want. But the question was about oneOf usage and syntax. – jruizaranguren Jan 10 '19 at 07:14
  • @jruizaranguren >"an empty object validates my schema, which is not probably what you want" - Well, your second schema has no required properties, so an empty object is valid against it. What if you added required properties? AFAIK, with oneOf, you create multiple object schema variants by adding the contents of a oneOf section to the surrounding schema node. All resulting schema variant must be valid schemas and for the object to be considered validated against the schema, it needs to be valid against exactly one variant. – Ark-kun Jan 11 '19 at 02:58
  • @Ark-kun, we can think of many what-if situations, and your explanations of json-schema are valid. But the question is about oneOf syntax, not about modeling a concrete problem. I find this discussion off-topic. – jruizaranguren Jan 11 '19 at 07:52
  • I guess I just do not understand what exactly you meant by ""oneOf" only applies to types. It is ignored for other types.". And I consider "it is convenient, not required." to be wrong. The `type` must be set either outside `oneOf` or inside all `oneOf` variants. Not omitted. Not specified both ways. – Ark-kun Jan 11 '19 at 22:33
  • ok, I understand the issue. By saying "it is convenient, not required" I mean the standard does not enforce its usage. As most of things in json-schema, it must be read as: "if your data is an object, then apply the oneOf constraint", "if your data is not an object, ignore this clause". – jruizaranguren Jan 12 '19 at 10:56
  • And the standard also says that each element in oneOf must be a valid json-schema. Thus, specifying the type is correct. It may help defining the constraints. – jruizaranguren Jan 12 '19 at 10:59
  • Can you have a oneOf _and_ properties on the top type? – justin.m.chase Jun 14 '21 at 13:19
  • @justin.m.chase, Should be that a new question to S.O.? – jruizaranguren Jun 26 '21 at 11:00
28

oneOf need to be used inside a schema to work.

Inside properties, it's like another property called "oneOf" without the effect you want.

Arian Kiehr
  • 413
  • 5
  • 13
  • 1
    I'm not saying your wrong, but can you provide a source for this information please? =] – Relequestual Jan 16 '15 at 10:44
  • 6
    Well it makes sense from deduction of what `properties` does, i.e. why would `properties` ever make a special case for keys named `oneOf`? Pretty much common sense it works this way now that I think of it. Thanks for the explanation. – davidtgq Oct 19 '17 at 00:17
  • This is what I found also. oneOf inside of properties does not work. – Moondoggy Jun 09 '21 at 04:08