How to make the root element mandatory in JSONSchema - jsonschema

I have the below JSONSchema, I want the root tag envelope to be mandatory.
Any help would be appreciated.
{
"id": "envelope",
"$schema": "http://json-schema.org/schema#",
"tittle": "Root schema",
"description": "Root schema for all services",
"apiVersion": "1.0",
"type": "object",
"required": [
"metadata",
"data"
],
"properties": {
"metadata": {
"description": "The meta data of the data field",
"type": "object",
"required": [
"sourceSystem",
"deliveryCount",
"retryPeriod",
"correlationId"
],
"properties": {
"sourceSystem": {
"description": "The source System ",
"type": "string"
},
"deliveryCount": {
"description": "Number of times the request tried",
"type": "number",
"default": 0,
"maxLength": 5
},
"retryPeriod": {
"description": "Time set to retry",
"type": "number"
},
"correlationId": {
"description": "Unique id for reference",
"type": "string"
}
}
},
"data": {
"description": "The actual content",
"type": "object"
},
"response": {
"description": "Response",
"type": "string"
}
}
}
The output is
{
"metadata": {
"sourceSystem": "",
"deliveryCount": 1,
"retryPeriod": 30,
"correlationId": ""
},
"data": {}
}
expected output is
{ "envelope" : {
"metadata": {
"sourceSystem": "",
"deliveryCount": 1,
"retryPeriod": 30,
"correlationId": ""
},
"data": {} } }

The "id" attribute does not define any root element, it is used for different purposes.
All you need to do is to define your root schema as an object with a single "envelope" property:
{
"type" : "object"
"properties" : {
"envelope" : {
// here comes your (current) schema
}
}
}

Related

Cloudformation for redis parameter group

I want to create a cloudformation template to deploy a redis parameter group. The problem lies in the fact that each of my parameters looks like this:
{
"Parameters": [
{
"ParameterName": "activedefrag",
"ParameterValue": "no",
"Description": "Enabled active memory defragmentation",
"Source": "user",
"DataType": "string",
"AllowedValues": "yes,no",
"IsModifiable": true,
"MinimumEngineVersion": "4.0.9",
"ChangeType": "immediate"
},
{
"ParameterName": "active-defrag-cycle-max",
"ParameterValue": "75",
"Description": "Maximal effort for defrag in CPU percentage",
"Source": "user",
"DataType": "integer",
"AllowedValues": "1-75",
"IsModifiable": true,
"MinimumEngineVersion": "4.0.9",
"ChangeType": "immediate"
}
}
Now I understand the basic format of the template but can't figure out how to pass in parameters as seen above.
Failing template:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Template to create a consul cluster",
"Parameters": {
"CacheParameterGroupFamily": {
"Description": "The name of the cache parameter group family that this cache parameter group is compatible with.",
"Type": "String",
"Default": "redis4.0",
"AllowedValues": ["memcached1.4", "memcached1.5", "redis2.6", "redis2.8", "redis3.2", "redis4.0", "redis5.0"]
},
"ParameterGroupDescription": {
"Description": "What this parameter group will be used for",
"Type": "String"
}
},
"Resources": {
"RedisParameterGroup": {
"Type": "AWS::ElastiCache::ParameterGroup",
"Properties": {
"CacheParameterGroupFamily" : { "Ref": "CacheParameterGroupFamily" },
"Description" : { "Ref": "ParameterGroupDescription" },
"Properties" : {
"Parameters": [{
"ParameterName": "activedefrag",
"ParameterValue": "no",
"Description": "Enabled active memory defragmentation",
"Source": "user",
"DataType": "string",
"AllowedValues": "yes,no",
"IsModifiable": true,
"MinimumEngineVersion": "4.0.9",
"ChangeType": "immediate"
}]
}
}
}
}
}
Welcome to StackOverflow!
You need to incorporate those parameters into the Parameters section of the template, and change the syntax so that it conforms to the Parameters format.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Template to create a consul cluster",
"Parameters": {
"CacheParameterGroupFamily": {
"Description": "The name of the cache parameter group family that this cache parameter group is compatible with.",
"Type": "String",
"Default": "redis4.0",
"AllowedValues": ["memcached1.4", "memcached1.5", "redis2.6", "redis2.8", "redis3.2", "redis4.0", "redis5.0"]
},
"ParameterGroupDescription": {
"Description": "What this parameter group will be used for",
"Type": "String"
},
"activedefrag": {
"Description": "Enabled active memory defragmentation",
"Type": "String",
"AllowedValues": ["yes", "no"],
"Default": "no"
},
"activedefragcyclemax": {
"Description": "Maximal effort for defrag in CPU percentage",
"Type": "Number",
"Default": 75,
"MinValue": 1,
"MaxValue": 75
}
},
"Resources": {
"RedisParameterGroup": {
"Type": "AWS::ElastiCache::ParameterGroup",
"Properties": {
"CacheParameterGroupFamily" : { "Ref": "CacheParameterGroupFamily" },
"Description" : { "Ref": "ParameterGroupDescription" },
"Properties" : {
"activedefrag": {"Ref": "activedefrag"},
"active-defrag-cycle-max": {"Ref": "activedefragcyclemax"}
}
}
}
}
}

Angular6 JSON schema form for Array of Items

I am trying out Angular6 JSON form for my application and stuck in the issue of having array schema
The basic layout looks like
{
"schema": {
"type": "array",
"properties": {
"type": { "type": "string" },
"number": { "type": "string" },
}
},
"layout": [
{
"type": "array",
"items": [ {
"type": "div",
"displayFlex": true,
"flex-direction": "row",
"items": [
{ "key": "type", "flex": "1 1 50px",
"notitle": true, "placeholder": "Type"
},
{ "key": "number", "flex": "4 4 200px",
"notitle": true, "placeholder": "Phone Number"
}
]
} ]
}
],
"data": [
{ "type": "cell", "number": "702-123-4567" },
{ "type": "work", "number": "702-987-6543" }
]
}
But I am not getting the expected outcome, that is Form is prefilled with the data
[
{ "type": "cell", "number": "702-123-4567" },
{ "type": "work", "number": "702-987-6543" }
]
Refer: https://hamidihamza.com/Angular6-json-schema-form/
Based on your code there are some parts you may need to revisit.
The schema type should be either an object or boolean based on the documentation http://json-schema.org/latest/json-schema-core.html
Within the schema section, it seems that you want the type and number to be your properties of a JSON instance. Having this you can only pass one instance of data to the framework to fill in your properties because the framework cannot decide on which value to use for your property of type string.
In case of looking for having an array of type and number, you can have a property like "phone number" with the type array. below is an example from angular6-json-schema flex layout example which I think you had as your reference.
"schema": {
...
"phone_numbers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": { "type": "string", "enum": [ "cell", "home", "work" ] },
"number": { "type": "string" }
},
"required": [ "type", "number" ]
}
And pass your data having something as follows:
"data": {
...
"phone_numbers": [
{ "type": "cell", "number": "702-123-4567" },
{ "type": "work", "number": "702-987-6543" }
],
}

How to prevent certain fields based on another fields value with JSON Schema validator

Depending on the salaryRange the user selects I need to validate differently by requiring some fields and rejecting others. I feel like its a combination of allOf and not but I can't seem to quite get it.
Scenario #1
User selects salaryRange(Hourly)
Require hourlyRate
Prevent the submission of fields feeOne and feeTwo
Scenario #2
User selects salaryRange(0-50k OR 50-100k)
Require feeOne and feeTwo
Prevent the submission of field hourlyRate
Here is my schema
{
"schema": "http://json-schema.org/draft-04/schema#",
"$id": "http://mysite/schemas/job.json#",
"title": "Job",
"description": "Create job",
"type": "object",
"properties": {
"title": { "type": "string" },
"description": { "type": "string" },
"salaryRange": { "enum": ["0-50k", "50-100k", "100-150k", "150-200k", "200-300k", "300k+", "nonExempt", "Hourly"] },
"hourlyRate": {
"type": "number",
"minimum": 0,
"maximum": 300
},
"feeOne": {
"type": "number",
"minimum": 0
},
"feeTwo": {
"type": "number",
"minimum": 0
}
} ,
"additionalProperties": false,
"required": [
"title",
"description",
"salaryRange"
]
}
You can use oneOf and not required to model all possible combinations.
Here is an example in js:
https://runkit.com/embed/cf8cra1mwvx3/
{
"schema": "http://json-schema.org/draft-04/schema#",
"$id": "http://mysite/schemas/job.json#",
"title": "Job",
"description": "Create job",
"type": "object",
"properties": {
"title": { "type": "string" },
"description": { "type": "string" },
"salaryRange": { "enum": ["0-50k", "50-100k", "100-150k", "150-200k", "200-300k", "300k+", "nonExempt", "Hourly"] },
"hourlyRate": {
"type": "number",
"minimum": 0,
"maximum": 300
},
"feeOne": {
"type": "number",
"minimum": 0
},
"feeTwo": {
"type": "number",
"minimum": 0
}
},
"oneOf": [
{
"description": "Disallow fees for hourly salary",
"properties": {
"salaryRange": { "enum": ["Hourly"] }
},
"required": ["hourlyRate"],
"allOf": [
{"not":{"required":["feeOne"]}},
{"not":{"required":["feeTwo"]}}
]
},
{
"description": "Disallow hourly rate for 0-50k, 50-100k salaries",
"properties": {
"salaryRange": { "enum": ["0-50k", "50-100k"] }
},
"required": ["feeOne", "feeTwo"],
"not":{"required":["hourlyRate"]}
},
{
"description": "Allow other cases",
"properties": {
"salaryRange": { "not" : {"enum": ["Hourly", "0-50k", "50-100k"] } }
}
}
],
"additionalProperties": false,
"required": [
"title",
"description",
"salaryRange"
]
}

Using a json schema in multiple layouts

I'm helping to build an interface that works with Json Schema, and I have a question about interface generation based on that schema. There are two display types - one for internal users and one for external users. Both are dealing with the same data, but the external users should see a smaller subset of fields than the internal users.
For example, here is one schema, it defines an obituary:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "",
"type": "object",
"required": [
"id",
"deceased"
],
"properties": {
"id": { "type": "string" },
"account": {
"type": "object",
"required": [
"name"
],
"properties": {
"id": { "type": "number" },
"name": { "type": "string" },
"website": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "string",
"maxLength": 0
}
]
},
"email": {
"anyOf": [
{
"type": "string",
"format": "email"
},
{
"type": "string",
"maxLength": 0
}
]
},
"address": {
"type": "object",
"properties": {
"address1": { "type": "string" },
"address2": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"postalCode": { "type": "string" },
"country": { "type": "string" }
}
},
"phoneNumber": {
"anyOf": [
{
"type": "string",
"format": "phone"
},
{
"type": "string",
"maxLength": 0
}
]
},
"faxNumber": {
"anyOf": [
{
"type": "string",
"format": "phone"
},
{
"type": "string",
"maxLength": 0
}
]
},
"type": { "type": "string" }
}
},
"deceased": {
"type": "object",
"required": [
"fullName"
],
"properties": {
"fullName": { "type": "string" },
"prefix": { "type": "string" },
"firstName": { "type": "string" },
"middleName": { "type": "string" },
"nickName": { "type": "string" },
"lastName1": { "type": "string" },
"lastName2": { "type": "string" },
"maidenName": { "type": "string" },
"suffix": { "type": "string" }
}
},
"description": { "type": "string" },
"photos": {
"type": "array",
"items": { "type": "string" }
}
}
}
Internal users would be able to access all the fields, but external users shouldn't be able to read/write the account fields.
Should I make a second schema for the external users, or is there a way to indicate different display levels or public/private on each field?
You cannot restrict acess to the fields defined in a schema, but you can have 2 schema files, one defining the "public" fields, and the other one defining the restricted fields plus including the restricted fields.
So
public-schema.json:
{
"properties" : {
"id" : ...
}
}
restricted-schema.json:
{
"allOf" : [
{
"$ref" : "./public-schema.json"
},
{
"properties" : {
"account": ...
}
}
]
}

JsonSchema is not validated with oneOf

Need help to find the bug with this schema. It has oneOf operator.
Schema is here :
`{
"type": "object",
"required": [
"type",
"body"
],
"properties": {
"type": {
"description": "type of the document to post",
"type": "string",
"enum": [
"123",
"456"
]
},
"body": {
"type": "object",
"description": "body",
"oneOf": [{
"$ref": "#/definitions/abc",
"$ref": "#/definitions/def"
}]
}
},
"definitions": {
"abc": {
"type": "array",
"description": "abc",
"properties" : {
"name" : { "type" : "string" }
}
},
"def": {
"type": "array",
"description": "users","properties" : {
"name" : { "type" : "string" }
}
}
}
}`
My Json is this :
`{
"type": "123",
"body": {
"abc": [{
"name": "test"
}]
}
}`
It does not validate with tv4 and I also tried this online tool. It works without oneOf operator. Otherwise it does not validate it any tool.
Edit :
After reading the answers I modified the schema. New schema is :
{
"type": "object",
"properties": {
"type": {
"description": "type of the document to post",
"type": "string",
},
"body": {
"type": "object",
"description": "body",
"properties": {
"customers": {
"type": "array"
}
},
"anyOf": [
{
"title": "customers prop",
"properties": {
"customers": {
"type": "array",
"description": "customers",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
}
}
}
]
}
}
}
And json is here
{
"type": "customer",
"body": {
"none": [
{
"name": "test"
}
]
}
}
But it validates. I want to enforce one of "customers" or "users" in the body. To test I have removed users from the body.
Pl help.
The issue is that the data is passing both of your sub-schemas. oneOf means "match exactly one" - if you want "match at least one", then use anyOf.
In fact, both of your sub-schemas will pass all data. The reason is that properties is ignored when dealing with arrays.
What you presumably wanted to do instead is specify properties for the items in the array. For this, you need the items keyword:
"definitions": {
"abc": {
"type": "array",
"items": {
"type": "object",
"properties" : {
"name" : { "type" : "string" }
}
}
}
}
(You'll also need to add some distinct constraints - at the moment, both the "abc" and "def" definitions are identical apart from description, which makes the oneOf impossible because it will always match both or neither.)
Since you have the type at root level, you probably want the oneOf statement to check that an object with type "customer" has customers in the body (even though I would suggest skipping the body and placing customers and users directly in root object).
This works with your example, will require that an object with type "customer" has a body with "customers", and to clarify the matching, I let customer have the property "name" while the user has "username":
{
"type": "object",
"properties": {
"type": { "type": "string" },
"body": {
"type": "object",
"properties": {
"customers": {
"type": "array",
"items": { "$ref": "#/definitions/customer" }
},
"users": {
"type": "array",
"items": { "$ref": "#/definitions/user" }
}
}
}
},
"definitions": {
"customer": {
"type": "object",
"properties": { "name": { "type": "string" } },
"required": [ "name" ]
},
"user": {
"type": "object",
"properties": { "username": { "type": "string" } },
"required": [ "username" ]
}
},
"oneOf": [
{
"properties": {
"type": {
"pattern": "customer"
},
"body": {
"required": [ "customers" ]
}
}
},
{
"properties": {
"type": {
"pattern": "user"
},
"body": {
"required": [ "users" ]
}
}
}
]
}
When using "type": "array" then the item type is defined in the "items" property not "properties" property... Also both types in oneOf are same, but only one must match.
Try
...
"definitions": {
"abc": {
"type": "array",
"description": "abc",
"items" : {
"name" : { "type" : "string" }
}
},
"def": {
"type": "array",
"description": "users",
"items" : {
"username" : { "type" : "string" }
}
}
}