Modelling GeoJSON in Swagger - json

I'm trying to add a definition for GeoJSON in a Swagger API that serves JSON. I'm running into some issues with defining the options that GeoJSON has for features.
A single feature can be a choice of "Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", or a "MultiPolygon". Each of these though imposes different constraints on their coordinates field. For instance, a point might have a single coordinate consisting of an longtitude latitude pai, while a Polygon might have a field that looks like this:
[ [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] ].
I've looked around but there seems no elegant solution to this problem except by just defining the geometry as a string instead of a complex object like this. This means a loss of information, and makes it undesirable to port APIs based on a Swagger specification because input strings will have to be parsed to an object first.
The GeoJSON objects look like this.
What I've got so far is this:
"Feature": {
"type": "object",
"properties": {
"id": {
},
"geometry": {
"$ref": "#/definitions/Geometry"
},
"type" : {
"type" : "string"
},
"properties": {
"type": "object"
}
}
}
And the geometry definition looks like this:
"Geometry": {
"type": "object",
"properties": {
"type":{
"type": "string",
"enum": ["Point","MultiPoint","LineString","MultiLineString","Polygon","MultiPolygon"]
},
"coordinates": {
"type": "array",
"items": {
"type": "number",
"example": [4.49965, 52.06891]
}
}
}
}

Have you checked this gist file: geometry_geojson-yaml

I'm afraid there's no way to describe that using Swagger. You can describe a single type, but not something that would accept/produce all types.

It seems to me that the most recent attempt is this one: https://app.swaggerhub.com/apis/OlivierMartineau/GeoJSON/1.0.1
It was created by an "OlivierMartineau" aka "zitoun" or "zit0un" who used the already mentioned (by #jmini) "gist" by "bubbobne" as well as one of the forks by "idkw" as the foundation for his model.

Related

Can't validate a json with a schema using an external schema file

My issue
I need to validate a json file from a Json Schema.
I have a main schema and this schema should load an external schema file to validate some part of the json.
For different reasons I can't merge them. I do need 2 schema file.
I am using a $ref statement as proposed in this doc:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/test-json?view=powershell-7.2&WT.mc_id=DT-MVP-5004831
but whatever I try I have this error:
Cannot parse the JSON schema.
What I need
What is the right schema syntax to do this in my case.
Test case
I have this directories on my disk:
.
test.ps1
----/jsons
params.json
----/schemas
securities.json
storages.json
I run validation from this pretty simple PowerShell (test.ps1):
# read json param
$testFile = Get-Content "./jsons/params.json" -Encoding UTF8 | convertfrom-json -Depth 50
$testee = $testFile.parameters | convertto-json
# read json schema
$schemasFile = (Get-ChildItem -Path "./schemas/storages.json").FullName
# test json from schema
$result = $testee | Test-Json -SchemaFile $schemasFile
$result
My json (params.json):
{
"parameters": {
"storages": [
{
"comment": "Very important storage",
"name": "fdlmsto",
"securities": [
{
"comment": "Critical rule",
"kind": "MSI"
}
]
}
]
}
}
The main schema (storages.json):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": [
"storages"
],
"properties": {
"storages": {
"$id": "#/properties/storages",
"type": "array",
"additionalProperties": false,
"items": [
{
"$id": "#/properties/storages/items",
"type": "object",
"additionalProperties": false,
"required": [
"name",
"comment"
],
"properties": {
"$ref": "#/schemas/securities.json",
"name": {
"type": "string",
"$id": "#/properties/storages/items/properties/name"
},
"comment": {
"type": "string",
"$id": "#/properties/storages/items/properties/comment"
}
}
}
]
}
}
}
and the child schema (securities.json):
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"additionalProperties": false,
"required": [
"securities"
],
"properties": {
"securities": {
"$id": "#/properties/securities",
"type": "array",
"additionalProperties": false,
"items": {
"$id": "/properties/securities/items",
"anyOf": [
{
"$id": "#/properties/securities/items/anyOf/0",
"type": "object",
"additionalItems": false,
"additionalProperties": false,
"required": [
"kind",
"comment"
],
"properties": {
"comment": {
"type": "string",
"$id": "#/properties/securities/items/anyOf/0/properties/comment"
},
"kind": {
"type": "string",
"$id": "#/properties/securities/items/anyOf/0/properties/kind"
}
}
}
]
}
}
}
}
What I tested
I tested several syntaxe for $ref:
#/schemas/securities.json
./schemas/securities.json
/schemas/securities.json
....
I also tried to set the schema in the repository of test.ps1
I checked that this is the securities section that don't work
Thank you
I edit the question to leave what worked for me:
Change storages.json like this:
"properties": {
"securities": {
"$ref": "securities1.json#/securities"
},
"name": {
"type": "string"
},
"comment": {
"type": "string"
}
}
Change securities.json as:
{
"securities": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"kind",
"comment"
],
"properties": {
"comment": {
"type": "string"
},
"kind": {
"type": "string"
}
}
}
}
}
And of course I applied all changed suggested in the post
It appears that the powershell cmdlet is powered by NJsonSchema, which at this stage doesn't support JSON Schema draft 2020-12.
There are several issues I see.
storages.json
"$schema": "https://json-schema.org/draft/2020-12/schema" declares this is a draft 2020-12 schema. As your schema doesn't use any 2020-12 features, I'd suggest using draft 7 as it'll definitely be supported by NJsonSchema.
Your schema is declaring itself to be the meta-schema with "$id": "https://json-schema.org/draft/2020-12/schema". The meta-schema is a special schema that validates other schemas. What you probably want here is just storages.json, or even better, a full URI like https://examp.le/schemas/storages.json. It's just an identifier though; it doesn't need to be downloadable.
Remove the internal $ids. They're only specifying relative location and not providing any more detail. This kind of thing is generally calculated at runtime.
Your $ref is wrong. #/schemas/securities.json is going to try to search for this location inside the current schema. You need a full URI here, and you'll want to declare that same URI as $id in securities.json as well. This may be what is producing the error.
At #/properties/storages, you have "type": "array" and "additionalProperties": false. Maybe you meant additionalItems? additionalProperties only works on objects. (I also see this in securities.json at #/properties/securities.)
Your use of items is an array. This will only validate the first item. I think you're wanting to validate all the items. If so, just use the schema as the value for items; don't wrap it in an array. (also seen in securities.json)
securities.json
This schema uses draft 4, which is quite old and a bit incompatible with later drafts. Specifically, I see it's using $id in several places, which replaced draft 4's id in draft 6. Since draft 4 doesn't recognize $id, it'll probably ignore them. Moreover, $ref-ing between drafts isn't something that's required (or perhaps even addressed) by the specification. (I added some tests to the test suite around this only about a week ago.) As with storages.json I'd suggest using draft 7 here.
As mentioned above, you'll want to add an $id to the root so that the $ref in storages.json can identify this schema properly.
As with storages.json, remove the interal $ids; they're not providing any additional function.
You use an anyOf but only declare a single schema. You don't need this. Just move the keywords in that subschema up to where the allOf is.
{
"type": "object",
"allOf": [ { ... } ]
}
// becomes
{
"type": "object",
...
}
Sorry this sounds like a pile-on. I realize you might be new with JSON Schema. The learning curve can be pretty steep.
Instead of the documentation provided by MS, I'd use Understanding JSON Schema for how to write schemas. Use the MS docs only for how to use the cmdlet.
Just to add one point to the excellent answer above: the array form of items is not valid in 2020-12 anyway - it was replaced with prefixItems.
But the advice to use draft-07 throughout is the right advice, as is the advice to use the schema form of items anyway.

filtering geoJSON data using JMESPath not working

I am trying to filter some data from the geoJSON data structure shown as below:
"features": [
{
"type": "Feature",
"properties": {
"#id": "node/7071544593",
"addr:city": "Joensuu",
"addr:housenumber": "12",
"addr:postcode": "80100",
"addr:street": "Siltakatu",
"addr:unit": "C 33",
"alt_name": "Crasman Oy Joensuu",
"alt_name_1": "Crasman Oy",
"name": "Crasman Joensuu",
"short_name": "Crasman",
"website": "https://www.crasman.fi"
},
"geometry": {
"type": "Point",
"coordinates": [
29.7621398,
62.6015236
]
},
"id": "node/7071544593"
},
{
"type": "Feature",
"properties": {
"#id": "node/7117872562",
"amenity": "car_rental",
"operator": "avis"
},
"geometry": {
"type": "Point",
"coordinates": [
29.7630643,
62.6036656
]
},
"id": "node/7117872562"
}
]
What I am trying to do is iterate through this array of features, look into the properties object to check if it contains website, if Yes, then I can print its coordinates from geometry object.
This is what I tried:
Features[*].properties[?contains(#,'website')=='true'].geometry.coordinates
It gives me null value
Try this:
features[?contains(keys(properties),'website')].geometry.coordinates
E.g.:
$ jp "features[?contains(keys(properties),'website')].geometry.coordinates" <input.json
[
[
29.7621398,
62.6015236
]
]
With regard to why your example didn't work:
Identifiers are case-sensitive, so you need features, not Features.
properties is an object, not an array, so you can't apply a filter expression to it.
Even if you could, it's not properties that you want to filter. You are trying to filter whole features.
contains tests if an array contains an item (or if a string contains a substring), not whether an object has a key. You can use keys() to get the keys of an object in an array.
You don't need to compare the result of contains() to true, it's already a boolean.
Even if you were trying to compare to true, you'd need to use backticks: `true`, not quotes 'true'.

Couchbase geospatial bounding box query

As far as I know, couchbase has support for geospatial data, but I am not sure how to use this.
Does anybody know, if it is possible to query for elements which are inside a bounding box via N1QL SELECT statement in couchbase?
If yes, how would a SELECT statement look like, to query with a bounding box for the elements in the following json?
{
"type": "Feature",
"properties": {
"name": "Bermuda Triangle",
"area": 1150180
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-64.73, 32.31],
[-80.19, 25.76],
[-66.09, 18.43],
[-64.73, 32.31]
]
]
}
}
{
"type": "Feature",
"properties": {
"name": "Flemish Diamond",
"area": 2947
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[3.55, 51.08],
[4.36, 50.73],
[4.84, 50.85],
[4.45, 51.30],
[3.55, 51.08]
]
]
}
}
{
"type": "Feature",
"properties": {
"name": "Research Triangle",
"area": 252
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-78.93, 36.00],
[-78.67, 35.78],
[-79.04, 35.90],
[-78.93, 36.00]
]
]
}
}
Couchbase Geospatial queries are only supported through Geospatial Views, not N1QL queries.
You would need to create Geospatial View for your coordinates and query it via the Views API.
You can see more information on Geospatial Views in the Couchbase Server documentation: https://developer.couchbase.com/documentation/server/current/indexes/querying-using-spatial-views.html
It is technically possible to emulate a geospatial query using N1QL, however it would not be backed by a spatial index and would incur a significant performance penalty - especially for polygon data like you've supplied. You can see an article on how to do this with point data here: https://dzone.com/articles/speed-up-spatial-search-in-couchbase-n1ql

JSON schema enum vs pattern for single value

I have an sample json:
{
"type": "persons",
"id": 2,
"attributes": {
"first": "something",
"second": "something else"
}
}
And I have to make a schema for it (using JSON API specs and JSON schema docs):
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"type": {
"type": "string",
"pattern": "^persons$"
},
"id": {
"type": "integer"
},
"attributes": {
"type": "object",
"properties": {...}
}
},
"required": ["type", "id", "attributes"]
}
And the question is: if the only acceptable value for "type" is "persons", should I use in schema pattern (like above) or enum like
"enum": ["persons"]
I couldn't get any clear answer from documentation, although in examples in specs enums are used for single values. So what's your opinion?
Ultimately, it doesn't really matter. Both will work and both are reasonable. That said, the most common approach I've seen is to use enum. Neither are perfect for readability, but I think enum is better for two reasons.
Using pattern requires two lines to express. Using enum requires only one because type is implied by the value in the array. Two lines are harder to read than one, so if that line is expressive enough, I say stick with one.
Not everyone is comfortable reading regex. enum might be more accessible for that reason.
Since draft-6 there is a new keyword called const for this use-case.
"type": {
"type": "string",
"const": "persons"
},
http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.1.3

How to make JSON Schema recognize a hierarchy of objects?

I'm writing a schema for JSON. Let's say I have an object called "feature". Inside this object there are various properties defined, one of which is "features", which is an array containing another set of "feature". So the hierarchy is virtually infinite.
{
"name": "feature",
"properties": {
"feature_name": {
"type": "string",
"description": "Name of feature",
"required": true
},
"feature_type": {
"type": "string",
"description": "Type of feature",
"required": true
},
"features": {
"type": "array",
"items": {
//How do I define the same object for an infinite hierarchy?
}
}
}
}
The "features" array could consist of many more "feature" objects, each of which may also have more "features". Can I accommodate for this in a JSON schema?
You are probably not going to be able to implement an infinite hierarchy. JSON.net seems to be one of the better solutions for this type of problem.