title: How do I require one field or another or (one of two others) but not all of them?
body: <p>I am having trouble coming up with a JSON schema that will validate if the JSON contains either:</p>
  
  <ul>
  <li>one field only</li>
  <li>another field only</li>
  <li>(one of two other fields) only</li>
  </ul>
  
  <p>but not to match when multiples of those are present.</p>
  
  <p>In my case specifically, I want one of </p>
  
  <ul>
  <li><code>copyAll</code></li>
  <li><code>fileNames</code></li>
  <li><code>matchesFiles</code> and/or <code>doesntMatchFiles</code></li>
  </ul>
  
  <p>to validate but I don't want to accept when more than that is there.</p>
  
  <p>Here's what I've got so far:</p>
  
  <pre><code>{
      "$schema": "http://json-schema.org/draft-04/schema#",
      "type": "object",
      "required": [ "unrelatedA" ],
      "properties": {
      "unrelatedA": {
          "type": "string"
      },
      "fileNames": {
          "type": "array"
      },
      "copyAll": {
          "type": "boolean"
      },
      "matchesFiles": {
          "type": "array"
      },
      "doesntMatchFiles": {
          "type": "array"
          }
      },
      "oneOf": [
           {"required": ["copyAll"], "not":{"required":["matchesFiles"]}, "not":{"required":["doesntMatchFiles"]}, "not":{"required":["fileNames"]}},
           {"required": ["fileNames"], "not":{"required":["matchesFiles"]}, "not":{"required":["doesntMatchFiles"]}, "not":{"required":["copyAll"]}},
           {"anyOf": [
                 {"required": ["matchesFiles"], "not":{"required":["copyAll"]}, "not":{"required":["fileNames"]}},
                 {"required": ["doesntMatchFiles"], "not":{"required":["copyAll"]}, "not":{"required":["fileNames"]}}]}
      ]
  } ;
  </code></pre>
  
  <p>This matches more than I want to. I want this to match all of the following:</p>
  
  <pre><code>{"copyAll": true, "unrelatedA":"xxx"}
  {"fileNames": ["aab", "cab"], "unrelatedA":"xxx"}
  {"matchesFiles": ["a*"], "unrelatedA":"xxx"}
  {"doesntMatchFiles": ["a*"], "unrelatedA":"xxx"}
  {"matchesFiles": ["a*"], "doesntMatchFiles": ["*b"], "unrelatedA":"xxx"}
  </code></pre>
  
  <p>but not to match:</p>
  
  <pre><code>{"copyAll": true, "matchesFiles":["a*"], "unrelatedA":"xxx"}
  {"fileNames": ["a"], "matchesFiles":["a*"], "unrelatedA":"xxx"}
  {"copyAll": true, "doesntMatchFiles": ["*b"], "matchesFiles":["a*"], "unrelatedA":"xxx"}
  {"fileNames": ["a"], "matchesFiles":["a*"], "unrelatedA":"xxx"}
  {"unrelatedA":"xxx"}
  </code></pre>
  
  <p>I'm guessing there's something obvious I'm missing - I'd like to know what it is.</p>