What is the best way to validate that all values in a dictionary adhere to a JSON schema? - jsonschema

I want to validate that all values in a dictionary adhere to a schema while the keys can be whatever. Is there a better way to do this than just using a pattern that matches everything:
"foo": {
"type": "object",
"patternProperties": {
"^.*$": {
"$ref": "#/$defs/bar"
}
}
}

Using patternProperties like you're doing is that best way, although you can simplify the regex to an empty string because regexes aren't implicitly anchored in JSON Schema.
{
"type": "object",
"patternProperties": {
"": { "$ref": "#/$defs/bar" }
}
}
The other option is to use additionalProperties, which is less verbose, but can lead to bugs.
{
"type": "object",
"additionalProperties": { "$ref": "#/$defs/bar" }
}
The problem with this is that it's only accidentally the behavior you really want. What you want is to validate all properties against this schema. additionalProperties validates only the properties that aren't declared with properties or patternProperties. If later someone wants to add an additional constraint to a specific property and adds properties, they're going to break this schema unless they rewrite the additionalProperties using patternProperties.
It's one of those small things that's an unlikely to occur, but when it does, it's been known to be a source of bugs.

I would suggest to use the additionalProperties keyword where you can also set a schema. More details here
Using patternProperties, the validator needs to run a RegExp check for each property which takes time.

Related

kotlinx.serialization Decode object from map value taking ID from the map key

Using koltinx.serialization, is there a way to enable the "value" serializer to take it's ID from the "key" of the map entry?
For example, given the following JSON:
{
"data": {
"key1": { "name": "value1" },
"key2": { "name": "value2" },
}
}
And the serializable data class:
#Serializable
data class DataEntry(val id: String, val name: String)
How can I write a KSerializer for the map (or for the map "value") that will enable the DataEntry serializer to take the value of it's id field from the map "key"?
The result being a List (or Map) of:
DataEntry("key1", "value1"),
DataEntry("key2", "value2"),
I know that I can deserialise the map using DataEntry(val name: String) (without id) and then build a list of entries with id's from that. But in my case, for the map "value" I actually have many large data types, and this would require duplicating a lot of code. I want a generic way to handle this scenario given any data type.

Is it possible to easily build DSL in Kotlin?

TypeScript allows very nice and clean and 100% type-safe way to build data-like DSLs. I wonder if it's possible in Kotlin?
For example, in TypeScript code below (playground) we defining columns for data table. It checks that values are correct (string enums), checks all the optional / required fields, has autocomplete etc. And it just works out of the box, all you need to do is define types.
Is it possible to use something like that in Kotlin? It's possible to use Java Builder-pattern, but it's not ideal, and it requires to write lots of code for builder-methods. Also, Kotlin doesn't have a way to use "number" enum, it would be Type.number, doesn't look nice. Or maybe I'm missing something and there's a way to build nice and clean DSL in Kotlin without too much boilerplate code?
// Defining DSL ---------------------------------------------
type Type = "string" | "number" | "boolean" | "unknown"
interface StringFormatOptions {
type: "string"
}
interface LineFormatOptions {
type: "line"
ticks?: number[]
}
interface Column {
type: Type
format?: StringFormatOptions | LineFormatOptions
}
// Using DSL ------------------------------------------------
const columns: Column[] = [
{
type: "number",
format: { type: "line", ticks: [1000] }
},
{
type: "string"
}
]
Yes, you can create type-safe DSLs in Kotlin. It may be tricky to understand at first, but it really become very easy when you get used to it.
It works by creating functions that receive lambdas which have a specific receiver type... Well... let's try again. Assuming you are the user of already existing DSL, this is what happens:
There is a function that requires providing a lambda to it.
You provide a lambda.
The function provides a this parameter of a specific type to your lambda.
You can use properties/functions of provided this object in the lambda, effectively making possible to go deeper into DSL chain.
Let's see this example:
fun copy(init: CopyBuilder.() -> Unit) { TODO() }
interface CopyBuilder {
var from: String
var to: String
fun options(init: CopyOptionsBuilder.() -> Unit) { TODO() }
}
interface CopyOptionsBuilder {
var copyAttributes: Boolean
var followSymlinks: Boolean
}
We have a copy() function which receives a lambda. Provided lambda will have access to CopyBuilder object as this, so it will have access to e.g. from and to properties. By calling options() from the lambda we move deeper and now we have access to CopyOptionsBuilder object.
copy() is responsible for providing a proper implementation of CopyBuilder object to your lambda. Similarly, implementation of options() need to provide a proper implementation of CopyOptionsBuilder. This was omitted from the example above.
Then it can be used as:
copy {
from = "source"
to = "destination"
options {
copyAttributes = true
followSymlinks = false
}
}
If you use Gradle with Kotlin DSL then build.gradle.kts file is actually a regular Kotlin file. It just starts with some variables provided to you. Another good example of DSL in Kotlin is kotlinx.html library. It generates HTML code with syntax like this:
html {
body {
div {
a("https://kotlinlang.org") {
target = ATarget.blank
+"Main site"
}
}
}
}
You can read more about this here: https://kotlinlang.org/docs/type-safe-builders.html

Using JSON Schema, how do I validate an object where keys and values are not known in advance?

i need to validate this format:
{
"variables": {
"team": "red",
"L3_HCU_TESTS": "N",
"dbo_user": "user",
"version": 920
}
}
I don't know in advance names of keys and values.
"variables" are not mandatory but if it's provided,
keys can be any string, and value can be any value.
What is important for me is to validate that "variables" (if provider) will include key and value.
Empty values are not allowed
With a JSON Object, the keys MUST be a string, so your requirement for string based keys is already covered by "is valid JSON".
Expanding your requirements, you want:
"variables" must be an object
There must be at least one property in the object
The value of the properties must not be null
Here's a schema which satisfies those requirements:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"variables": {
"type": "object",
"minProperties": 1,
"additionalProperties": {
"not": {
"type": "null"
}
}
}
}
}
You can test it against your instance using this link.
"type": "object" makes sure that "variables" is an object.
"minProperties" makes sure there is a minimum number of properties in an object.
"additionalProperties" value subschema is applied to all key values in the object (as "properties" and "patternProperties" are not defined). The subschema inverts the result of a subschema, which defines the type must be null. The inversion results in, must be NOT NULL.
I haven't seen minProperties used very often. Here's the reference link for that JSON Schema keyword: https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-6.5.2

Json schema provides a default for an object but not for its properties

I have a property somewhere in my JSON schema that is of type "object" and has a default value - an object with its properties and their values. Inside the object schema I have the properties listed and their types specified - but no defaults for them. The question is - in what will this property result if only one of the values is provided but non of the rest? Will they default to what they would if noting was provided?
"autoStart": {
"type": "object",
"default":{
"foo": true,
"bar": 3
},
"properties":{
"foo": {
"type": "boolean"
},
"bar": {
"type": "number"
}
}
}
If the provided json is
"autoStart": {
"foo": false
}
what will happen to the "bar"?
What does default do? Nothing according to the specification
default is an annotation keyword. An annotation is to provide information to an implementation or application so they can define their own additional behaviour. This means any behaviour which does ANYTHING with default is implementation or application specific.
Let's look at what the spec says...
There are no restrictions placed on the value of this keyword.
When multiple occurrences of this keyword are applicable to a
single sub-instance, implementations SHOULD remove duplicates.
This keyword can be used to supply a default JSON value associated
with a particular schema. It is RECOMMENDED that a default value be
valid against the associated schema.
https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-10.2
Besides annotation collection and return, there's nothing an implementation should be expected to DO with an annotation keyword, apart from that which it defines in its own documentation.

How can I specify that an object's type is one of several possible object types?

I have an API where I post a set of object to the server, differentiated by type, where each type has some different parameters. The commands are structured like so:
{
"type": <command-name>,
"args": {
<command-specific args>
}
}
For example, these may be two possible commands:
{
"type": "Flooblinate",
"args": {
"intensity": "High",
"frequency": "Every blue moon"
}
}
{
"type": "Blagostrate",
"args": {
"temperature": 34.5,
"darkMatter": true
}
}
How can I specify this in Swagger? I can specify an enum for "type", but how do I say ""args" is one of these possible objects"?
I've checked out the docs but nothing stands out. The most promising one was allOf because it displayed nicely in the editor (pastebin, paste into the online editor):
definitions:
Product:
type: object
allOf:
- type: object
title: Flooblinate
properties:
intensity:
type: string
frequency:
type: string
- type: object
title: Blagostrate
properties:
temperature:
type: number
darkMatter:
type: boolean
Which looks like this:
However, this isn't semantically what I need, and, not surprisingly, in the online viewer (that one's not set up for my test case, not sure how to link up a local file easily), they are presented as if all the fields appear at the same time, which is of course what allOf means:
What's the proper way to represent this with Swagger?
According to input from Ron on the google group, what I want is to use the discriminator property:
definitions:
Product:
type: object
discriminator: type
properties:
type: string
required: [type]
Flooblinate:
allOf:
- $ref: '#/definitions/Product'
- type: object
properties:
intensity:
type: string
frequency:
type: string
Blagostrate:
allOf:
- $ref: '#/definitions/Product'
- type: object
properties:
temperature:
type: number
darkMatter:
type: boolean
Semantically this means what I want it to mean. I should specify $ref: '#/definitions/Product' wherever the API accepts or returns any one of Flooblinate or Blagostrate. Note this requires a field (called type here) on the objects to act as the discriminator.
I thought this may be the approach, but the tools didn't show what I expected. However:
That's because the tools are not 100% in support of the discriminator yet - however, that's the right way to describe your use case.
Once you define the discriminator in the top level model, anything that 'allOf's it will be considered a viable option, and indeed you're refer to the top level model for usage.