add field conditionally after traversing all nodes in JSON Circe Scala - json

Assume I have a complex JSON:
{
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"birthday": { "type": "string", "format": "date" },
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"country": { "type" : "string" }
}
}
}
}
I want to add the field additionalProperties: false to all the json objects having type:object
In the above example, the output json would look like:
{
"type": "object",
"additionalProperties": false,
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"birthday": { "type": "string", "format": "date" },
"address": {
"type": "object",
"additionalProperties": false,
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"country": { "type" : "string" }
}
}
}
}
This is what I have so far:
val circeJson: io.circe.Json = parser.parse(json).getOrElse(io.circe.Json.Null)
val updatedJson = transform(circeJson)
def transform(js: io.circe.Json): io.circe.Json =
js
.mapArray(_.map(transform))
.mapObject(obj => obj.+:("additionalProperties", io.circe.Json.fromBoolean(false)))
From some reason, it's working and adding the field only for the root level object.

The following is working:
def transformJson(js: io.circe.Json, transformation: JsonObject => JsonObject): io.circe.Json =
js
.mapArray(_.map(transformJson(_, transformation)))
.mapObject(obj => transformation(obj).mapValues(obj => transformJson(obj, transformation)))
val circeJson: io.circe.Json = parser.parse(json).getOrElse(io.circe.Json.Null)
val updatedJson =
transformJson(circeJson, obj => obj.+:("additionalProperties", io.circe.Json.fromBoolean(false)))

Related

Json Schema: Require a property only when a specific property is present

good day everyone, I am new here,
I have a json response looking like this
{
"Number": "20.09.00001",
"Supplier": {
"Name": "John Doe",
"Title": "Mr.",
"FirstName": "John",
"LastName": "Doe",
"Phone": "0212341234",
"Email": "foobar#gmail.com",
"Code": "Foo123",
"Gender": "Male"
}
}
I want to make inside the supplier properties required so I make a JSON schema validation looking like this
{
"type": "object",
"required": [
"Supplier"
],
"properties": {
"Supplier": {
"type": "object",
"required": [
"Name",
"Title",
"FirstName",
"LastName",
"Phone",
"Email",
"Code",
"Gender"
],
"properties": {
"Name": {
"type": "string"
},
"Title": {
"type": "string"
},
"FirstName": {
"type": "string"
},
"LastName": {
"type": "string"
},
"Phone": {
"type": "string"
},
"Email": {
"type": "string"
},
"Code": {
"type": "string"
},
"Gender": {
"type": "string"
}
}
}
}
}
but the problem is, the inside of supplier properties are not always present, when it's not supplied, it will only return empty objects like this
{
"Number": "20.09.00001",
"Supplier": {}
}
how can I validate only IF the inside supplier returns full property object, and ignore if the supplier returns an empty object?
I have tried using if else and anyOff but resulting in no luck.
my code with if else that did not work:
"Supplier": {
"type": ["object"],
"if": {
"properties": {
"Name": {
"type": ["string", "null"]
},
"Title": {
"type": ["string", "null"]
},
"FirstName": {
"type": ["string", "null"]
},
"LastName": {
"type": ["string", "null"]
},
"Phone": {
"type": ["string", "null"]
},
"Email": {
"type": ["string", "null"]
},
"Code": {
"type": ["string", "null"]
},
"Gender": {
"type": ["string", "null"]
}
}
},
"then": {
"required": [
"Name",
"Title",
"FirstName",
"LastName",
"Phone",
"Email",
"Code",
"Gender"
]
},
"else": {
"required": []
}
}
I think anyOf is the best approach in this case. The object is either empty or all the properties are required. There are a couple of way to assert that an object is empty. You could use "maxProperties": 0 or "const": {}.
{
"type": "object",
"properties": {
... all your properties ...
},
"anyOf": [
{ "const": {} },
{ "required": [ ... all required properties ... ] }
]
}

How to add additionalProperties: false to every object in a json-schema Scala

Assume I have a complex json-schema
{
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"birthday": { "type": "string", "format": "date" },
"address": {
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"country": { "type" : "string" }
}
}
}
}
I want to add additionalProperties: false to ALL object in the schema.
I'm using "com.typesafe.play" %% "play-json" % "2.7.4" for handling JSON in my application. Is there an easy way to to so? Are there any other libraries I can use?
I managed to do it in the following way:
val strictJson = json.replaceAll("\"type\": \"object\",\n", "\"type\": \"object\",\n\"additionalProperties\": false,\n")
obviously it is not a good solution, I would like to do it with the Json library

JSON Schema for strict objects in an array

I have created a JSON schema to validate a simple JSON file. The good news is that it validates in the way that I intended, in that any number of booking elements can appear in any order, and no extra properties are allowed in each type of booking element.
Ideally I would like to remove the full list of possible properties in a bookingElement object (id, type, depair, destair, city) in the JSON schema, and just leave the oneOf lists, which show clearly which fields are allowed in each different type of element.
Can anyone provide a version of the schema without that full list that still applies the strict rules?
This is the JSON:
{
"bookingElements": [
{
"id" : "00003",
"type" : "flight",
"depair" : "LHR",
"destair" : "CDG"
},
{
"id" : "00008",
"type" : "hotel",
"city" : "Paris"
}
]
}
The schema is:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"additionalProperties": false,
"properties": {
"bookingElements": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"depair": {
"type": "string"
},
"destair": {
"type": "string"
},
"city": {
"type": "string"
}
},
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"depair": {
"type": "string"
},
"destair": {
"type": "string"
}
}
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"city": {
"type": "string"
}
}
}
]
}
}
},
"required": [
"bookingElements"
]
}
Ideally the JSON schema would look something closer to the following:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"additionalProperties": false,
"properties": {
"bookingElements": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"depair": {
"type": "string"
},
"destair": {
"type": "string"
}
}
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"city": {
"type": "string"
}
}
}
]
}
}
},
"required": [
"bookingElements"
]
}

Postman test - object's schema

I have a response body:
{
"Id": 15,
"Name": "Carrier1",
"Fein": "Fein1",
"McNumber": "McNumber1",
"DotNumber": "DotNumber1",
"Address": {
"Street": "Street1",
"City": "City1",
"ZipPostalCode": null,
"StateName": "AA (Armed Forces Americas)",
"StateAbbr": "AA",
"ContactName": null,
"ContactPhone": null,
"ContactFaxNumber": null,
"ContactEmail": null
}
}
I use Postman and want to describe schema for validation in tests:
const schema = {
"required": ["Id"],
"properties": {
"Id": {
"type": "integer",
},
"Name": {
"type": "string",
},
"Fein": {
"type": "string",
},
"McNumber": {
"type": "string",
},
"DotNumber": {
"type": "string",
},
"Address": {
"type" : {
"properties": {
"Street": {
"type": "string",
},
},
}
}
}
};
var carrier = JSON.parse(responseBody);
tests["Carrier is valid"] = tv4.validate(carrier, schema);
but it does not work. Validation that it just should be object:
"Address": {
"type" : "object"
}
works fine. How to describe it details?
Would this work:
const schema = {
"required": ["Id"],
"properties": {
"Id": {
"type": "integer"
},
"Name": {
"type": "string"
},
"Fein": {
"type": "string"
},
"McNumber": {
"type": "string"
},
"DotNumber": {
"type": "string"
},
"Address": {
"type" : "object",
"properties": {
"Street": {
"type": "string"
}
}
}
}
}
Added this test to check:
pm.test('Schema Valid', () => {
var carrier = pm.response.json()
pm.expect(tv4.validate(carrier, schema)).to.be.true
})
I'm using the native Postman application so if you're still using the Chrome extension, this will fail due to it not knowing about the pm.* API functions

JSON Schema: Require properties on an optional object type

I have defined a customer object type in my json schema:
"customer": {
"type": "object",
"properties": {
"id": { "type": "string" },
"first_name": { "type": "string" },
"last_name": { "type": "string"},
"email": { "type": "string" },
"billing_address": { "$ref": "#\/definitions\/street_address" },
"shipping_address": { "$ref": "#\/definitions\/street_address" },
},
"required": [ "id", "first_name", "last_name", "email", "billing_address"]
},
I would like to validate shipping_address (optional object) if it is sent and reject it if it is missing the required fields. Here is the street_address object definition:
"street_address": {
"type": "object",
"properties": {
"first_name": {"type": "string" },
"last_name": { "type": "string" },
"address": { "type": "string" },
"address2": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zip_code": { "type": "string"},
"country_code": { "type": "string"},
"phone": { "type": "string"},
"fax": {"type": "string"}
},
"required": [
"first_name",
"last_name",
"address",
"city",
"state",
"zip_code",
"country_code"
]
},
How do I configure my JSON schema to accomplish this? When I send a shipping address now, the fields inside the object do not get validated.
You are using the reference "$ref": "#\/definitions\/street_address" (btw, you dont have to escape the slashes). In that case the street_address definition must be in in the same document, inside the "defintions" node. So your schema file looks like this
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "customer",
"type": "object",
"properties": {
"id": { "type": "string" },
"first_name": { "type": "string" },
"last_name": { "type": "string"},
"email": { "type": "string" },
"billing_address": { "$ref": "#/definitions/street_address" },
"shipping_address": { "$ref": "#/definitions/street_address" },
},
"required": [ "id", "first_name", "last_name", "email", "billing_address"],
"definitions" : {
"street_address" : {
/* here comes the street_address definition */
},
/* other entity definitions */
}
}
I'm using the nodejs module jayschema (see https://github.com/natesilva/jayschema) for validation, and it works fine that way.