Fix wrong JSON values with Ajv - jsonschema

I am trying to fix property values when they have different value than specified.
JSON:
{
"stone": "bri"
}
I want to fix the stone property to have "brick" if it is not already "brick".
Schema:
{
"required": [
"stone"
],
"properties": {
"stone": {
"type": "string",
"if": {
"not": {
"constant": "brick"
}
},
"then": { "default": "brick" }
}
}
}
Using v4.11.4:
var ajv = new Ajv({ useDefaults: true, v5: true });
require('ajv-keywords')(ajv);
Is there any other keyword that I can use to basically change the value to the good one, or to an other value by reference?

Solved with a custom keyword:
ajv.addKeyword('modify_current', {
modifying: true,
validate: function (schema_parameter_value, validated_parameter_value, validation_schema_object, current_data_path, validated_parameter_object, validated_parameter) {
validated_parameter_object[validated_parameter] = schema_parameter_value;
return true;
},
errors: false
});
And in the schema:
"then": { "modify_current": "brick" }

Related

Can JSON Schema if statements handle nested $refs?

I have a JSON Schema using draft 2020-12 and I am trying to use an if-else subschema to check that a particular property exists based on the value of another property. Below is the if statement I am currently using. There are more properties but I have have omitted them for the sake of brevity. They are identical except the type of the property in the then statement is different. They are all wrapped in an allOf array:
{
"AValue": {
"allOf": [
{
"if": {
"$ref": "#/$defs/ValueItem/properties/dt",
"const": "type1"
},
"then": {
"properties": {
"string": { "type": "string" }
},
"required": ["string"]
}
}
]
}
}
The #/$defs/ValueItem/properties/dt being referred to is here:
{
"ValueItem": {
"properties": {
"value": {
"$ref": "#/$defs/AValue"
},
"dt": {
"$ref": "#/$defs/DT"
}
},
"additionalProperties": false
}
}
#/$defs/DT is here:
{
"DT": {
"type": "string",
"enum": [
"type1",
"type2",
"type3",
"type4"
]
}
}
I expected that when dt is encountered in a JSON instance document, the validator will check if the value of dt is type1 and then validate that an additional property called string is also present and is of type string. However, what actually happens is the validator complains that "Property 'string' has not been defined and the schema does not allow additional properties".
I assume that this is because the condition in the if statement evaluates to false so the subschema is never applied. However, I am unsure why that would be as I followed the example here when creating the if-then-else block. The only thing I can think of that is different is the use of $ref which I have in my schema but it is not in the example.
I found this answer which makes me think that it is possible to use $ref in an if statement but is it possible to use a ref that points to another ref or am I thinking about it incorrectly?
I have also tried removing the $ref from the if statement like below but it still doesn't work. Is it because of the $ref in the properties?
{
"AValue": {
"properties": {
"dt": {
"$ref": "#/$defs/DT"
}
},
"required": [
"dt"
],
"allOf": [
{
"if": {
"properties": {
"dt": {
"const": "type1"
}
}
},
"then": {
"properties": {
"string": {
"type": "string"
}
}
}
}
]
}
}
The problem is not cascading the $ref keywords. The const keyword at the if statement is not applied to the target of the $ref, but to the current location in the JSON input data. In this case to "AValue". To check if the property "dt" is of value "type1" at this point, you would need an if like this (simple solution with no $ref):
"if": {
"properties": {
"dt": {
"const": "type1"
}
},
"required": [ "dt" ]
}
Edit: Showing complete JSON Schema and error in JSONBuddy with $ref:

JSON Schema - anyOf within conditional?

I am trying to define a JSON schema with conditionals. I built an MVE which already doesn't work as I expect it.
The object I want to validate is:
{
"keiner": false,
"abdominal": true,
"zervikal": false
}
The conditional rule is simple. When "keiner" is true, both other values have to be false. If "keiner" is false, at least one of the other two has to be true.
I wrote this schema:
{
"type": "object",
"properties": {
"keiner": { "type": "boolean" },
"abdominal": { "type": "boolean" }
},
"if": {
"properties": {
"keiner": { "const": true }
}
},
"then": {
"properties" : {
"abdominal": { "const": false },
"zervikal": {"const": false }
}
},
"else": {
"properties": {
"anyOf": [
{ "abdominal": { "const": true } },
{ "zervikal": { "const" : true } }
]
}
}
}
But the Newtonsoft online validator gives the error message
Unexpected token encountered when reading value for 'anyOf'. Expected StartObject, Boolean, got StartArray.
for the line in which ´anyOf´ starts. This confuses me, as all examples I can find show anyOf followed by an array of options.
So what am I doing wrong? Why cannot I have a startArray after anyOf, and how do I write the schema correctly?
I guess this is the schema you are looking for:

JSON schema - require a nested field

var schema = {
"id": "test.json#",
"definitions": {
"body": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
}
},
"request": {
"properties": {
"user": {
"$ref": "#/definitions/body"
}
}
}
},
"typeA": {
"$ref": "#/definitions/request"
},
"typeB": {
"allOf": [
{"$ref": "#/definitions/request"},
{"required": ["name"]} // Should require the name field in the user object.
]
}
}
var Ajv = require('ajv')
var ajv = Ajv({
schemas: [schema]
})
var data = {
user: {
"name": "tom",
"age": 1
}
}
var valid = ajv.validate({$ref: "test.json#/typeA"}, data)
console.log('VALID', valid) // Returns true.
var valid = ajv.validate({$ref: "test.json#/typeB"}, data)
console.log('VALID', valid) // Returns false.
I'm new to JSON schemas. I'd like to reuse the definitions-request-schema in typeA and typeB. typeB should require the name field in the user-object. How do I achieve this? I know why my solution above is not working. Required should be something like "user.name".
Thanks you for your help.

Max Response Limitation im OTA_AirLowFareSearchRQ

I'm working with Sabre REST API. I have a issue with the OTA_AirLowFareSearchRQ, I try limit the response number using the MaxResponses in the json structure but seems that I make something wrong because the response give to me 95 answers in the cert environment (https://api.cert.sabre.com/).
The json request that I use is:
{
"OTA_AirLowFareSearchRQ": {
"Target": "Production",
"PrimaryLangID": "ES",
"MaxResponses": "15",
"POS": {
"Source": [{
"RequestorID": {
"Type": "1",
"ID": "1",
"CompanyName": {}
}
}]
},
"OriginDestinationInformation": [{
"RPH": "1",
"DepartureDateTime": "2016-04-01T11:00:00",
"OriginLocation": {
"LocationCode": "BOG"
},
"DestinationLocation": {
"LocationCode": "CTG"
},
"TPA_Extensions": {
"SegmentType": {
"Code": "O"
}
}
}],
"TravelPreferences": {
"ValidInterlineTicket": true,
"CabinPref": [{
"Cabin": "Y",
"PreferLevel": "Preferred"
}],
"TPA_Extensions": {
"TripType": {
"Value": "Return"
},
"LongConnectTime": {
"Min": 780,
"Max": 1200,
"Enable": true
},
"ExcludeCallDirectCarriers": {
"Enabled": true
}
}
},
"TravelerInfoSummary": {
"SeatsRequested": [1],
"AirTravelerAvail": [{
"PassengerTypeQuantity": [{
"Code": "ADT",
"Quantity": 1
}]
}]
},
"TPA_Extensions": {
"IntelliSellTransaction": {
"RequestType": {
"Name": "10ITINS"
}
}
}
}
}
MaxResponses could be something for internal development which is part of the schema but does not affect the response.
What you can modify is in the IntelliSellTransaction. You used 10ITINS, but the values that will work should be 50ITINS, 100ITINS and 200ITINS.
EDIT2 (as Panagiotis Kanavos said):
RequestType values depend on the business agreement between your company and Sabre. You can't use 100 or 200 without modifying the agreement.
"TPA_Extensions": {
"IntelliSellTransaction": {
"RequestType": {
"Name": "50ITINS"
}
}
}
EDIT1:
I have searched a bit more and found:
OTA_AirLowFareSearchRQ.TravelPreferences.TPA_Extensions.NumTrips
Required: false
Type: object
Description: This element allows a user to specify the number of itineraries returned.

ElasticSearch:filtering documents based on field length?

Is there a way to filter ElasticSearch documents based on the length of a specific field?
For instance, I have a bunch of documents with the field "body", and I only want to return results where the number of characters in body is > 1000. Is there a way to do this in ES without having to add an extra column with the length in the index?
Use the script filter, like this:
"filtered" : {
"query" : {
...
},
"filter" : {
"script" : {
"script" : "doc['body'].length > 1000"
}
}
}
EDIT
Sorry, meant to reference the query DSL guide on script filters
You can also create a custom tokenizer and use it in a multifields property as in the following:
PUT test_index
{
"settings": {
"analysis": {
"analyzer": {
"character_analyzer": {
"type": "custom",
"tokenizer": "character_tokenizer"
}
},
"tokenizer": {
"character_tokenizer": {
"type": "nGram",
"min_gram": 1,
"max_gram": 1
}
}
}
},
"mappings": {
"person": {
"properties": {
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
},
"words_count": {
"type": "token_count",
"analyzer": "standard"
},
"length": {
"type": "token_count",
"analyzer": "character_analyzer"
}
}
}
}
}
}
}
PUT test_index/person/1
{
"name": "John Smith"
}
PUT test_index/person/2
{
"name": "Rachel Alice Williams"
}
GET test_index/person/_search
{
"query": {
"term": {
"name.length": 10
}
}
}