parrish / json-schema_builder Goto Github PK
View Code? Open in Web Editor NEWBuild JSON schemas with Ruby
License: MIT License
Build JSON schemas with Ruby
License: MIT License
The spec allows one to declare
{
"links": {
"description": "A resource object **MAY** contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.",
"type": "object",
"properties": {
"self": {
"description": "A `self` member, whose value is a URL for the relationship itself (a \"relationship URL\"). This URL allows the client to directly manipulate the relationship. For example, it would allow a client to remove an `author` from an `article` without deleting the people resource itself.",
"type": "string",
"format": "uri"
},
"related": {
"$ref": "#/definitions/link"
}
},
"additionalProperties": true
}
}
Hey!
I'm trying to split up the rather clunky schema for JSONAPI into parts and combine them per use case.
It seems possible to assign a nested object definition as items to any array.
But when i try to add an object to another object with a name it's properties stay empty
{"type"=>:object, "properties"=>{"data"=>{"type"=>"object", "properties"=>{}}}}
Is there a way or would i have to add something like a properties method to object that behave like items on array ?
Thanks,
Ralf
In JSON Schema, it is possible to define object properties based on regular expressions, even while still using fixed keys:
{
"type": "object",
"properties": {
"builtin": { "type": "number" }
},
"patternProperties": {
"^S_": { "type": "string" },
"^I_": { "type": "integer" }
}
}
Looking into the object class, I tried to translate this to your DSL as follows:
class MySchema
require 'json/schema_builder'
include JSON::SchemaBuilder
def schema
object do
pattern_properties do
string '^S_'
string '^I_'
end
number :builtin
end
end
end
puts JSON.pretty_generate(MySchema.new.schema.as_json)
This only leads to everything outside pattern_properties
to be generated:
{
"type": "object",
"properties": {
"builtin": {
"type": "number"
}
}
}
My next try was to specify the pattern properties as option for object
, but this only lead to patternProperties
without the actual patterns:
object pattern_properties: [string('^S_')] do
...
end
{
"type": "object",
"patternProperties": [
{
"type": "string"
}
],
"properties": {
"builtin": {
"type": "number"
}
}
}
Could you give me an example of how to correctly use pattern arguments in your DSL?
Thanks in advance!
First, great gem!
It would be nice though to be able to pass Instances of Builders ( for example JSON::SchemaBuilder::Object
) around and build them up from the outside. Example:
def post(obj)
obj.string :title
obj.string :body
end
obj = JSON::SchemaBuilder::Object.new
post(obj)
This way you could keep your definition logic in POROs (which some prefer).
I realize this is already somewhat possible, but the Builders Schema is only updated on initialization (as far as I can see).
So simply updating at each entity
call could be enough.
Hey! First of all, thank you for providing a (currently: the) DSL to generate JSON schemas in Ruby!
I solved this problem by now (see below), but I'll include my original issue message here anyway as it might make for a good addition to the examples
I ran into a problem today when describing a rather complex schema that included an array of polymorphic objects.
From my understanding of JSON schema, it should be possible to simply define "items" using the "anyOf"
keyword and then specifying the various objects which might occur as array items. I built a small example:
class MySchema
require 'json/schema_builder'
include JSON::SchemaBuilder
def schema
object do
array :an_array do
items do
any_of [
object { string(:name) },
object { number(:age) }
]
end
end
end
end
end
puts JSON.pretty_generate(MySchema.new.schema.as_json)
This indeed generates a corresponding anyOf
, but it also adds a type
and properties
to items
:
{
"type": "object",
"properties": {
"an_array": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
{
"type": "object",
"properties": {
"age": {
"type": "number"
}
}
}
],
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "number"
}
}
}
}
}
}
I also tried to give items
an explicit type: :object
which got rid of the additonal keys, but always included an additional empty object definition inside anyOf
:
{
"type": "object",
"properties": {
"an_array": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
}
},
{
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
{
"type": "object",
"properties": {
"age": {
"type": "number"
}
}
}
]
}
}
}
}
Solution:
I solved this now by specifying the possible objects using
items any_of: [
object { string(:name) },
object { number(:age) }
]
which results in the correct output.
Shouldn't any_of
as an option to items
and when being used inside items
have the same result? At least it looked this way from the terse/verbose examples.
It would be nice if we were able to add additional keywords, like
object do
string :name
string :notes, 'x-scope': :admin
end
In this example I'm adding a key called x-scope
, to indicate that it's a custom attribute. But it might as well be scope
without the x-
.
The resulting JSON would look like this
{
"type": "object",
"properties": {
"name": { "type": "string" },
"notes": { "type": "string", "x-scope": "admin" }
}
}
This is allowed according to the spec. See chapter "Extending JSON Schema". Although it's in draft, if I'm correct.
https://json-schema.org/draft/2019-09/json-schema-core.html#extending
Do you think this is something you'd like to add?
If yes, I'm willing to implement it and create a Pull Request.
When you have an object and you force required: false
on at least one attribute, but none are required : true
, it will still creates a "required" property with an empty array
schema = object do
string :my_key, required: false
string :my_other_key, required: true
end.as_json
# => {"type"=>"object", "required"=>["my_other_key"], "properties"=>{"my_key"=> {"type"=>"string"}, "my_other_key"=>{"type"=>"string"}}}
schema = object do
string :my_key
string :my_other_key
end.as_json
# => {"type"=>"object", "properties"=>{"my_key"=>{"type"=>"string"}, "my_other_key"=>{"type"=>"string"}}}
schema = object do
string :my_key
string :my_other_key, required: false
end.as_json
# => {"type"=>"object", "required"=>[], "properties"=>{"my_key"=>{"type"=>"string"}, "my_other_key"=>{"type"=>"string"}}}
Hi again ๐
today I ran into a problem when using the functionality implemented in #8: If I build a schema by extending an existing object instead of nesting everything and assigning null: true
, a StackLevelTooDeep
exception will be thrown.
As this seems to only happen with null
as attribute which is internally translated to an any_of
, I would suspect that it has something to do with this.
I will investigate a bit further myself, but maybe you have an idea what exactly is causing this instantly.
I find myself having a few problems following the gem's working due to its architecture.
The following is the smallest example I could produce that still causes the error.
It will print the stacktrace once its size reaches 3000.
#!/usr/bin/env ruby
require 'json/schema_builder'
trace_func_proc = proc do |event, file, line, id, binding, classname|
if event == 'call' && caller.length > 3000
puts caller
exit(1)
end
end
set_trace_func(trace_func_proc)
class SchemaStack
include JSON::SchemaBuilder
def schema
obj = object :bar, null: true
obj.string :baz
obj
end
end
SchemaStack.new.schema.as_json
The repeating portion seems to be:
/Users/stex/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/json-schema_builder-0.8.2/lib/json/schema_builder/entity.rb:141:in `extract_types'
/Users/stex/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/json-schema_builder-0.8.2/lib/json/schema_builder/object.rb:30:in `extract_types'
/Users/stex/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/json-schema_builder-0.8.2/lib/json/schema_builder/object.rb:35:in `reinitialize'
/Users/stex/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/json-schema_builder-0.8.2/lib/json/schema_builder/attribute.rb:16:in `block in attribute'
/Users/stex/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/json-schema_builder-0.8.2/lib/json/schema_builder/entity.rb:142:in `extract_types'
/Users/stex/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/json-schema_builder-0.8.2/lib/json/schema_builder/entity.rb:46:in `initialize'
/Users/stex/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/json-schema_builder-0.8.2/lib/json/schema_builder/dsl.rb:11:in `new'
/Users/stex/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/json-schema_builder-0.8.2/lib/json/schema_builder/dsl.rb:11:in `entity'
/Users/stex/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/json-schema_builder-0.8.2/lib/json/schema_builder/dsl.rb:45:in `block (2 levels) in register'
Thanks a lot in advance!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.