Home > front end >  Unable to constrain properties to specific object with oneOf
Unable to constrain properties to specific object with oneOf

Time:07-23

I have 3 kinds of product definitions. I want to express choose each product based on its properties. I define the products and then use a oneOf to conditionally pick the product. However, the attributes for the other products are not constrained and can be placed in the json they shouldn't be allowed in. Here is my attempt at displaying the selected product.

{
  "type": "object",
  "required": [
    "type",
    "productId"
  ],
  "additionalProperties": false,
  "properties": {
    "productId": {
      "type": "string",
      "minLength": 1
    },
    "type": {
      "enum": ["product"]
    },
    "productInfo": {
      "type": "object",
      "additionalProperties": false,
      "required": [ "type", "gameMode"],
      "properties": {
        "type": {
          "enum": ["digital"]
        },
        "gameMode": {
          "type": "object",
          "oneOf": [
            { "$ref": "#/$defs/audioPlayer" },
            { "$ref": "#/$defs/multiPlayer" },
            { "$ref": "#/$defs/console" },
            { "$ref": "#/$defs/controller" }
          ]
        }
      }
    }
  },
  "$defs": {
    "audioPlayer": {
      "type": "object",
      "gameMode": {
        "enum": ["mp3", "wav"]
      }
    },
    "controller": {
      "type": "object",
      "properties": {
        "mode": {
          "enum": ["Retro","XboxElite" ]
        },
        "batteryLevel": { "type": "string" },
        "warranty": { "type": "string" },
        "tradeIn": { "type": "boolean" }
      }
    },
    "multiPlayer": {
      "type": "object",
      "properties": {
        "mode": {
          "enum": ["LiveXBox","FortNite"]                  
        },
        "signUp": {
          "type": "object",
          "properties": {
            "url": {   "type": "string" }
          }
        }
      }
    },
    "console": {
      "type": "object",
      "properties": {
        "mode": {
          "enum": ["single", "multi"]
        },
        "required": {
          "type": "boolean"
        }
      }
    }
  }
}

This is the JSON that validates successfully against the schema, which is undesirable. I only want the properties to be based on the product.

//This product has properties it shouldn't have
// signUp should be only allowed in multiplayer etc.      
{
  "type": "product",
  "productId": "wwwwww",
  "productInfo": {
    "type": "digital",
    "gameMode": {
      "mode": "mp3",
      "required": true,
      "signUp": "https://mmmmmmm.com",
      "warranty": "yes"
    }        
  }
}

The correct examples should be:

// Audio Player properties only
{
  "type": "product",
  "productId": "audio",
  "productInfo": {
    "type": "digital",
    "gameMode": {
      "mode": "mp3",  // or "wav"
    }        
  }
}
// Controller properties only
{
  "type": "product",
  "productId": "controller",
  "productInfo": {
    "type":"digital",
    "gameMode": {
      "mode": "Retro", // or XboxElite
      "batteryLevel": "high",
      "warranty": "yes",
      "tradeIn": true
    }        
  }
}

CodePudding user response:

First of all, your $defs/audioPlayer schema is missing the properties keyword. That's not the problem, but I wanted to point it out so it doesn't confuse you later.

The actual problem is that you're missing the "additionalProperties": false constraint on your gameMode types. Your first example doesn't fail because additional properties are ignored by default just like any other schema.

  • Related