Search code examples
rjsonjsonschema

Multiple level nesting in JSON schema validation with sub-folders


I am trying to validate a json file with nested references where the children references are in a sub-folder of the parent one. The files are organised as following:

root/schemas/user
root/schemas/sub-schemas/address
root/schemas/sub-schemas/city

and they are referenced user -> sub-schemas/address -> sub-schemas/city

I am doing this in R with the package {jsonvalidate} but I guess my question is more about the way references work in the json-schema, rather than R specific. Here's the reproducible code in R which gives the error:

library(jsonvalidate)
library(testthat)

json_to_validate = '
{
  "address":{
    "city":"Firenze"
  }
}
'

root = file.path(tempdir(), "jsonvalidate")
dir = "schemas"
subdir = "sub-schemas"

dir.create(file.path(root, dir, subdir), recursive = TRUE, showWarnings = FALSE)

user_file = "user"
user_schema_path = file.path(root, dir, user_file)

address_file = "address"
address_rel_path = file.path(subdir, address_file)

city_file = "city"
city_rel_path = file.path(subdir, city_file)

user_schema_ref = sprintf('
{
  "$schema": "http://json-schema.org/draft-07/schema",
  "$id": "file://%s",
  "type":"object",
  "required":["address"],
  "properties":{
    "address":{
      "$ref": "%s"
    }
  }
}
', user_schema_path, address_rel_path)


address_schema = sprintf('
{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type":"object",
  "properties":{
    "city":{
      "$ref": "%s"
    }
  }
}
', city_rel_path)


city_schema = sprintf('
{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type":"string",
  "enum":["Firenze"]
}
')

write(user_schema_ref, user_schema_path)
write(address_schema, file.path(root, dir, address_rel_path))
write(city_schema, file.path(root, dir, city_rel_path))

list.files(root, recursive = TRUE)
#> [1] "schemas/sub-schemas/address" "schemas/sub-schemas/city"   
#> [3] "schemas/user"

cat(user_schema_ref)
#> 
#> {
#>   "$schema": "http://json-schema.org/draft-07/schema",
#>   "$id": "file:///var/folders/yq/82fd9hrj1zs7kx2hqbqlg9280000gp/T//Rtmpguupsl/jsonvalidate/schemas/user",
#>   "type":"object",
#>   "required":["address"],
#>   "properties":{
#>     "address":{
#>       "$ref": "sub-schemas/address"
#>     }
#>   }
#> }
cat(address_schema)
#> 
#> {
#>   "$schema": "http://json-schema.org/draft-07/schema",
#>   "type":"object",
#>   "properties":{
#>     "city":{
#>       "$ref": "sub-schemas/city"
#>     }
#>   }
#> }
cat(city_schema)
#> 
#> {
#>   "$schema": "http://json-schema.org/draft-07/schema",
#>   "type":"string",
#>   "enum":["Firenze"]
#> }

json_validate(entry1, user_schema_path, engine = "ajv",
              verbose = TRUE, greedy = TRUE, 
              error = FALSE)
#> Error in context_eval(join(src), private$context, serialize, await): Error: can't resolve reference sub-schemas/city from id sub-schemas/address

Created on 2022-08-24 with reprex v2.0.2

I managed to make it work if I put the files all in the same folder:

root/schemas/user
root/schemas/address
root/schemas/city

And I reference as following:

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "$id": "file:///var/folders/yq/82fd9hrj1zs7kx2hqbqlg9280000gp/T//Rtmpguupsl/jsonvalidate/schemas/user",
  "type":"object",
  "required":["address"],
  "properties":{
    "address":{
      "$ref": "address"
    }
  }
}

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type":"object",
  "properties":{
    "city":{
      "$ref": "city"
    }
  }
}

But, as soon as I move the sub-schemas in a sub-folder I can't make it work.


Solution

  • It appears that the issue was specific to the R package jsonvalidate and the issue has been resolved in PR #62. The reference child2 in the example below is resolved against child1 so it should not contain the sub folder.

    library(jsonvalidate)
    parent = c(
      '{',
      '  "type":"object",',
      '  "required":["child1"],',
      '  "properties":{',
      '    "child1":{',
      '      "$ref":"sub/child1.json"',
      '    }',
      '  }',
      '}'
    )
    child1 = c(
      '{',
      '  "type":"object",',
      '  "properties":{',
      '    "child2":{',
      '      "$ref":"child2.json"',
      '    }',
      '  }',
      '}'
    )
    child2 = c(
      '{',
      '  "type":"string",',
      '  "enum":["test"]',
      '}'
    )
    
    dir = file.path(tempdir(), "jsonvalidate_test")
    schemadir = file.path(dir, "schemas")
    schemasubdir1 = file.path(schemadir, "sub")
    schemasubdir2 = file.path(schemasubdir1)
    dir.create(schemasubdir2, recursive = TRUE, showWarnings = FALSE)  
    writeLines(parent, file.path(schemadir, "parent.json"))  
    writeLines(child1, file.path(schemasubdir1, "child1.json"))  
    writeLines(child2, file.path(schemasubdir2, "child2.json"))  
    list.files(schemadir, recursive = TRUE)
    #> [1] "parent.json"     "sub/child1.json" "sub/child2.json"
    
    json_validate('{"child1":{"child2":"test"}}', file.path(schemadir, "parent.json"), engine = "ajv")
    #> [1] TRUE
    json_validate('{"child1":{"child2":""}}', file.path(schemadir, "parent.json"), engine = "ajv")
    #> [1] FALSE
    
    
    unlink(dir, recursive = TRUE)  
    

    Created on 2022-09-21 with reprex v2.0.2