2

I'm familiar with json schemas for arrays of a single object type. E.g. for this json example:

{
  "arr": [
    {
      "type": 1,
      "Steps": {
        "steps": 3500
      }
    },
    {
      "type": 1,
      "Steps": {
        "steps": 4000
      }
    }
  ]
}

...the associated schema format (that I've been introduced to) might be:

{
  "$schema": "https://json-schema.org/draft/2019-09/schema",
  "type": "object",
  "properties": {
    "arr": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "integer"
          },
          "Steps": {
            "type": "object",
            "properties": {
              "steps": {
                "type": "integer"
              }
            }
          }
        }
      }
    }
  }
}

But how can I adapt this schema to allow for objects of different types, e.g.

{
  "arr": [
    {
      "type": 1,
      "Steps": {
        "steps": 3500
      }
    },
    {
      "type": 2,
      "HeartRate": {
        "heartrates": 4000
      }
    }
  ]
}

?
Example adapted from Parse json array with different objects to their classes

I'd like the schema validation to pass at https://www.jsonschemavalidator.net/

I don't understand why, but the second example above passes at the noted online json schema validator using the above schema. Regardless of why it passes, I don't think the schema is correct, because it doesn't represent that there can be "Heartrate" objects.


I did read JSON Schema - array of different objects, but I believe this is not a dup, because that Q&A is about "Swagger 2.0" documents, which I'm not working with.


Update:

Would this be a reasonable/correct schema?

{
  "$schema": "https://json-schema.org/draft/2019-09/schema",
  "type": "object",
  "properties": {
    "arr": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "integer"
          },
          "Steps": {
            "type": "object",
            "properties": {
              "steps": {
                "type": "integer"
              }
            }
          },
          "HeartRate": {
            "type": "object",
            "properties": {
              "heartrates": {
                "type": "integer"
              }
            }
          }
        }
      }
    }
  }
}

Does the second schema correctly identify that "arr is an array of objects that have an integer named type an integer, and either an object named Steps or an object named Heartrate"?

StoneThrow
  • 5,314
  • 4
  • 44
  • 86

2 Answers2

2

Does the second schema correctly identify that "arr is an array of objects that have an integer named type an integer, and either an object named Steps or an object named Heartrate"?

Yes, somewhat. You do not state which properties must exist, and which properties cannot exist together at the same time, which we can do with the "required" and "oneOf" keywords.

Here's an updated schema, and also fixing an error you made in the "Heartrate" section (you said "steps" instead of "heartrates"):

{
  "$schema": "https://json-schema.org/draft/2019-09/schema",
  "type": "object",
  "properties": {
    "arr": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [ "type" ],
        "oneOf": [
           { "required": ["Steps"] },
           { "required": ["HeartRate"] }
        },
        "properties": {
          "type": {
            "type": "integer"
          },
          "Steps": {
            "type": "object",
            "required": ["steps"],
            "properties": {
              "steps": {
                "type": "integer"
              }
            }
          },
          "HeartRate": {
            "type": "object",
            "required": ["heartrates"],
            "properties": {
              "heartrates": {
                "type": "integer"
              }
            }
          }
        }
      }
    }
  }
}

Ether
  • 53,118
  • 13
  • 86
  • 159
  • Thanks for pointing out the "heartrates" mistake -- corrected that in the question now, for the benefit of future readers. – StoneThrow Feb 11 '21 at 03:38
1

Well, it passes because technically it is valid, you can have any combination of objects in an array. This schema explicitly states that there can be a heartrate object. You say you're familiar with arrays of a single object type, but what you should know is an array doesn't care what types of objects are in it, which is why your introduced schema is in fact valid. Whether or not it fits the function you're trying to achieve is another question all together.

It would imply that you would need either one very complex loop to traverse the array when you need to pull an object, assuming there will be so many that you can't adequately index them, or you would need some sort of index with address to the objects in the array, which might be cumbersome to write but possible. So the question is, what are you intending to do with these objects? Seems obvious from the properties you're using, seems like a fitness monitor of some sort, but then what your intention to do with the data isn't apparent so it's difficult to say what you could do even if this schema is valid, which keep in mind that it is indeed a valid structure for an array.

Although if you do have two types of objects, one could ask why not simply have an array for each type which will have supporting data pulling functions specific to their contents. But again it asks what you're intending to do.

Hope this helps your thought process.

UPDATE...

Not knowing exactly what you are intending to do, here is what I suggest you use as your data-structure format.

let someArray = [
  {
    Steps: {
      steps: 3500
    },
    HeartRate: {
      heartrates: 4000
    }
  },
  {
    Steps: {
      steps: 3500
    },
    HeartRate: {
      heartrates: 4000
    }
  }
]

You examples have some redundancy in them it seems, where they are explicitly stating that something is an object and something is a property of that object. JavaScript already knows all that so no need to add more complexity to the functions you create to work with these that will then have to dig through all those layers. I suggest that you create one object and each has a property of the type you've declared that is an object and that object can if you want be expanded or condensed as you desire. This will mitigate a lot of complexity to your structure. And so you'll see that you have an Array of Objects and those objects contain properties representing the data you're intending to gather.

But this assumes that the data you're gathering is from multiple sources. Again would need to know a bit more about what you're trying to accomplish.

Andrew
  • 399
  • 6
  • 15
  • 1
    Let me paraphrase to see if I understand correctly: a json array can natively have any combination of objects. But by adding an object description to the schema, I'm just explicitly stating that the array _may_ have an object of that type, but that does not mean that the array _must_ have an object of that type or preclude it from having objects of other types -- would it be correct of me to say that? – StoneThrow Feb 10 '21 at 23:03
  • 1
    Yes that would be correct, except its not called a JSON array, it would be an Array of Objects, the objects being in JSON format. – Andrew Feb 10 '21 at 23:09
  • 1
    I just saw your updated question, the Schema you showed also isn't an Array, it is an object, in JSON format with a property inside that is called arr which can possibly function like an Array but technically is not an Array. If your goal is to grow or shrink your objects at runtime you should know its a pain adding and removing properties in objects programmatically, but it is for sure possible. – Andrew Feb 10 '21 at 23:11